summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLee Garrett <lgarrett@rocketjump.eu>2023-12-12 11:18:06 +0100
committerLee Garrett <lgarrett@rocketjump.eu>2023-12-12 11:18:06 +0100
commit97e0b65ea0ed1bbb4720997f75ed248dc643747d (patch)
treed495d3a01b40c453487a190b37c8d3d1fd050ce9
parent8468671bb7631cbf042f8a196ece69efa47d9e81 (diff)
parentc4f015da4ac75017b97c24ef6601bdd98872e60f (diff)
downloaddebian-ansible-core-97e0b65ea0ed1bbb4720997f75ed248dc643747d.zip
Update upstream source from tag 'upstream/2.14.13'
Update to upstream version '2.14.13' with Debian dir 604c40cb59266d5479b16a75f0237db2fcbed79f
-rw-r--r--PKG-INFO2
-rwxr-xr-xbin/ansible-pull7
-rw-r--r--changelogs/CHANGELOG-v2.14.rst53
-rw-r--r--changelogs/changelog.yaml62
-rwxr-xr-xlib/ansible/cli/pull.py7
-rw-r--r--lib/ansible/module_utils/ansible_release.py2
-rw-r--r--lib/ansible/module_utils/common/json.py4
-rw-r--r--lib/ansible/modules/pip.py2
-rw-r--r--lib/ansible/parsing/yaml/dumper.py6
-rw-r--r--lib/ansible/playbook/conditional.py9
-rw-r--r--lib/ansible/playbook/task.py24
-rw-r--r--lib/ansible/plugins/action/assert.py23
-rw-r--r--lib/ansible/plugins/callback/__init__.py4
-rw-r--r--lib/ansible/plugins/filter/core.py5
-rw-r--r--lib/ansible/plugins/lookup/first_found.py15
-rw-r--r--lib/ansible/release.py2
-rw-r--r--lib/ansible/template/__init__.py15
-rw-r--r--lib/ansible/utils/unsafe_proxy.py265
-rw-r--r--lib/ansible_core.egg-info/PKG-INFO2
-rw-r--r--lib/ansible_core.egg-info/SOURCES.txt9
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml16
-rwxr-xr-xtest/integration/targets/ansible-pull/runme.sh5
-rwxr-xr-xtest/integration/targets/ansible-test-container/runme.py9
-rw-r--r--test/integration/targets/ansible-vault/roles/test_vault_embedded/tasks/main.yml2
-rw-r--r--test/integration/targets/ansible-vault/roles/test_vault_file_encrypted_embedded/tasks/main.yml2
-rw-r--r--test/integration/targets/apt_repository/tasks/apt.yml10
-rw-r--r--test/integration/targets/assert/assert.out.nested_tmpl.stderr4
-rw-r--r--test/integration/targets/assert/assert.out.nested_tmpl.stdout12
-rw-r--r--test/integration/targets/assert/assert.out.quiet.stderr (renamed from test/integration/targets/assert/assert_quiet.out.quiet.stderr)0
-rw-r--r--test/integration/targets/assert/assert.out.quiet.stdout (renamed from test/integration/targets/assert/assert_quiet.out.quiet.stdout)0
-rw-r--r--test/integration/targets/assert/nested_tmpl.yml9
-rw-r--r--test/integration/targets/assert/quiet.yml4
-rwxr-xr-xtest/integration/targets/assert/runme.sh3
-rw-r--r--test/integration/targets/command_shell/tasks/main.yml2
-rw-r--r--test/integration/targets/copy/tasks/tests.yml42
-rwxr-xr-xtest/integration/targets/debug/runme.sh2
-rw-r--r--test/integration/targets/debug/unsafe.yml13
-rw-r--r--test/integration/targets/dnf/tasks/test_sos_removal.yml2
-rw-r--r--test/integration/targets/expect/tasks/main.yml2
-rw-r--r--test/integration/targets/file/tasks/main.yml2
-rw-r--r--test/integration/targets/file/tasks/state_link.yml2
-rw-r--r--test/integration/targets/filter_urls/tasks/main.yml7
-rw-r--r--test/integration/targets/find/tasks/main.yml12
-rw-r--r--test/integration/targets/gathering_facts/test_gathering_facts.yml4
-rw-r--r--test/integration/targets/git/tasks/depth.yml2
-rw-r--r--test/integration/targets/git/tasks/localmods.yml4
-rw-r--r--test/integration/targets/git/tasks/submodules.yml14
-rw-r--r--test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml4
-rw-r--r--test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml8
-rw-r--r--test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml8
-rw-r--r--test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml8
-rw-r--r--test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml8
-rw-r--r--test/integration/targets/include_vars/tasks/main.yml28
-rw-r--r--test/integration/targets/lookup_ini/test_lookup_properties.yml2
-rw-r--r--test/integration/targets/lookup_subelements/tasks/main.yml6
-rw-r--r--test/integration/targets/loop_control/inner.yml4
-rw-r--r--test/integration/targets/module_precedence/modules_test_multiple_roles.yml2
-rw-r--r--test/integration/targets/module_precedence/modules_test_multiple_roles_reverse_order.yml2
-rw-r--r--test/integration/targets/module_precedence/multiple_roles/bar/tasks/main.yml2
-rw-r--r--test/integration/targets/module_precedence/multiple_roles/foo/tasks/main.yml2
-rw-r--r--test/integration/targets/script/tasks/main.yml4
-rw-r--r--test/integration/targets/slurp/tasks/main.yml2
-rw-r--r--test/integration/targets/subversion/aliases1
-rw-r--r--test/integration/targets/subversion/roles/subversion/defaults/main.yml5
-rw-r--r--test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml14
-rw-r--r--test/integration/targets/subversion/roles/subversion/tasks/setup.yml75
-rw-r--r--test/integration/targets/subversion/roles/subversion/tasks/tests.yml32
-rw-r--r--test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j211
-rw-r--r--test/integration/targets/subversion/vars/Alpine.yml3
-rw-r--r--test/integration/targets/subversion/vars/RedHat.yml1
-rw-r--r--test/integration/targets/subversion/vars/Ubuntu-20.yml2
-rw-r--r--test/integration/targets/template/tasks/main.yml2
-rw-r--r--test/integration/targets/unarchive/tasks/test_missing_binaries.yml2
-rw-r--r--test/integration/targets/unarchive/tasks/test_mode.yml8
-rw-r--r--test/integration/targets/unarchive/tasks/test_unprivileged_user.yml2
-rw-r--r--test/integration/targets/unarchive/tasks/test_zip.yml2
-rw-r--r--test/integration/targets/wait_for/tasks/main.yml8
-rw-r--r--test/lib/ansible_test/_data/completion/remote.txt2
-rw-r--r--test/lib/ansible_test/_data/completion/windows.txt4
-rw-r--r--test/lib/ansible_test/_internal/cgroup.py2
-rw-r--r--test/support/integration/plugins/modules/htpasswd.py275
-rw-r--r--test/units/parsing/yaml/test_dumper.py7
82 files changed, 786 insertions, 466 deletions
diff --git a/PKG-INFO b/PKG-INFO
index e994e044..84fd5acd 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: ansible-core
-Version: 2.14.11
+Version: 2.14.13
Summary: Radically simple IT automation
Home-page: https://ansible.com/
Author: Ansible, Inc.
diff --git a/bin/ansible-pull b/bin/ansible-pull
index dc8f055b..47084989 100755
--- a/bin/ansible-pull
+++ b/bin/ansible-pull
@@ -29,6 +29,7 @@ from ansible.plugins.loader import module_loader
from ansible.utils.cmd_functions import run_cmd
from ansible.utils.display import Display
+
display = Display()
@@ -102,8 +103,8 @@ class PullCLI(CLI):
'This is a useful way to disperse git requests')
self.parser.add_argument('-f', '--force', dest='force', default=False, action='store_true',
help='run the playbook even if the repository could not be updated')
- self.parser.add_argument('-d', '--directory', dest='dest', default=None,
- help='absolute path of repository checkout directory (relative paths are not supported)')
+ self.parser.add_argument('-d', '--directory', dest='dest', default=None, type=opt_help.unfrack_path(),
+ help='path to the directory to which Ansible will checkout the repository.')
self.parser.add_argument('-U', '--url', dest='url', default=None, help='URL of the playbook repository')
self.parser.add_argument('--full', dest='fullclone', action='store_true', help='Do a full clone, instead of a shallow one.')
self.parser.add_argument('-C', '--checkout', dest='checkout',
@@ -134,7 +135,6 @@ class PullCLI(CLI):
hostname = socket.getfqdn()
# use a hostname dependent directory, in case of $HOME on nfs
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):
raise AnsibleOptionsError("%s is not a valid or accessible directory." % options.dest)
@@ -307,6 +307,7 @@ class PullCLI(CLI):
if context.CLIARGS['purge']:
os.chdir('/')
try:
+ display.debug("removing: %s" % context.CLIARGS['dest'])
shutil.rmtree(context.CLIARGS['dest'])
except Exception as e:
display.error(u"Failed to remove %s: %s" % (context.CLIARGS['dest'], to_text(e)))
diff --git a/changelogs/CHANGELOG-v2.14.rst b/changelogs/CHANGELOG-v2.14.rst
index 54c5c51a..41be1326 100644
--- a/changelogs/CHANGELOG-v2.14.rst
+++ b/changelogs/CHANGELOG-v2.14.rst
@@ -5,6 +5,59 @@ ansible-core 2.14 "C'mon Everybody" Release Notes
.. contents:: Topics
+v2.14.13
+========
+
+Release Summary
+---------------
+
+| Release Date: 2023-12-11
+| `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+
+Minor Changes
+-------------
+
+- ansible-test - Add FreeBSD 13.2 remote.
+- ansible-test - Removed `freebsd/13.1` remote.
+
+Bugfixes
+--------
+
+- unsafe data - Address an incompatibility when iterating or getting a single index from ``AnsibleUnsafeBytes``
+- unsafe data - Address an incompatibility with ``AnsibleUnsafeText`` and ``AnsibleUnsafeBytes`` when pickling with ``protocol=0``
+
+v2.14.12
+========
+
+Release Summary
+---------------
+
+| Release Date: 2023-12-04
+| `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+
+Minor Changes
+-------------
+
+- ansible-test - Windows 2012 and 2012-R2 instances are now requested from Azure instead of AWS.
+
+Breaking Changes / Porting Guide
+--------------------------------
+
+- assert - Nested templating may result in an inability for the conditional to be evaluated. See the porting guide for more information.
+
+Security Fixes
+--------------
+
+- templating - Address issues where internal templating can cause unsafe variables to lose their unsafe designation (CVE-2023-5764)
+
+Bugfixes
+--------
+
+- ansible-pull now will expand relative paths for the ``-d|--directory`` option is now expanded before use.
+- ansible-test - Fix parsing of cgroup entries which contain a ``:`` in the path (https://github.com/ansible/ansible/issues/81977).
+
v2.14.11
========
diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml
index 3bde3c50..97eb4c13 100644
--- a/changelogs/changelog.yaml
+++ b/changelogs/changelog.yaml
@@ -938,6 +938,68 @@ releases:
- jinja_plugin_cache_cleanup.yml
- winrm-send-input.yml
release_date: '2023-10-03'
+ 2.14.12:
+ changes:
+ release_summary: '| Release Date: 2023-12-04
+
+ | `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+ '
+ codename: C'mon Everybody
+ fragments:
+ - 2.14.12_summary.yaml
+ release_date: '2023-12-04'
+ 2.14.12rc1:
+ changes:
+ breaking_changes:
+ - assert - Nested templating may result in an inability for the conditional
+ to be evaluated. See the porting guide for more information.
+ bugfixes:
+ - ansible-pull now will expand relative paths for the ``-d|--directory`` option
+ is now expanded before use.
+ - 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-11-27
+
+ | `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+ '
+ security_fixes:
+ - templating - Address issues where internal templating can cause unsafe variables
+ to lose their unsafe designation (CVE-2023-5764)
+ codename: C'mon Everybody
+ fragments:
+ - 2.14.12rc1_summary.yaml
+ - ansible-test-cgroup-split.yml
+ - ansible-test-windows-2012-and-2012-R2.yml
+ - cve-2023-5764.yml
+ - pull_unfrack_dest.yml
+ release_date: '2023-11-27'
+ 2.14.13:
+ changes:
+ bugfixes:
+ - unsafe data - Address an incompatibility when iterating or getting a single
+ index from ``AnsibleUnsafeBytes``
+ - unsafe data - Address an incompatibility with ``AnsibleUnsafeText`` and ``AnsibleUnsafeBytes``
+ when pickling with ``protocol=0``
+ minor_changes:
+ - ansible-test - Add FreeBSD 13.2 remote.
+ - ansible-test - Removed `freebsd/13.1` remote.
+ release_summary: '| Release Date: 2023-12-11
+
+ | `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+ '
+ codename: C'mon Everybody
+ fragments:
+ - 2.14.13_summary.yaml
+ - ci_freebsd_new.yml
+ - fbsd13_1_remove.yml
+ - unsafe-fixes-2.yml
+ release_date: '2023-12-11'
2.14.1rc1:
changes:
bugfixes:
diff --git a/lib/ansible/cli/pull.py b/lib/ansible/cli/pull.py
index dc8f055b..47084989 100755
--- a/lib/ansible/cli/pull.py
+++ b/lib/ansible/cli/pull.py
@@ -29,6 +29,7 @@ from ansible.plugins.loader import module_loader
from ansible.utils.cmd_functions import run_cmd
from ansible.utils.display import Display
+
display = Display()
@@ -102,8 +103,8 @@ class PullCLI(CLI):
'This is a useful way to disperse git requests')
self.parser.add_argument('-f', '--force', dest='force', default=False, action='store_true',
help='run the playbook even if the repository could not be updated')
- self.parser.add_argument('-d', '--directory', dest='dest', default=None,
- help='absolute path of repository checkout directory (relative paths are not supported)')
+ self.parser.add_argument('-d', '--directory', dest='dest', default=None, type=opt_help.unfrack_path(),
+ help='path to the directory to which Ansible will checkout the repository.')
self.parser.add_argument('-U', '--url', dest='url', default=None, help='URL of the playbook repository')
self.parser.add_argument('--full', dest='fullclone', action='store_true', help='Do a full clone, instead of a shallow one.')
self.parser.add_argument('-C', '--checkout', dest='checkout',
@@ -134,7 +135,6 @@ class PullCLI(CLI):
hostname = socket.getfqdn()
# use a hostname dependent directory, in case of $HOME on nfs
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):
raise AnsibleOptionsError("%s is not a valid or accessible directory." % options.dest)
@@ -307,6 +307,7 @@ class PullCLI(CLI):
if context.CLIARGS['purge']:
os.chdir('/')
try:
+ display.debug("removing: %s" % context.CLIARGS['dest'])
shutil.rmtree(context.CLIARGS['dest'])
except Exception as e:
display.error(u"Failed to remove %s: %s" % (context.CLIARGS['dest'], to_text(e)))
diff --git a/lib/ansible/module_utils/ansible_release.py b/lib/ansible/module_utils/ansible_release.py
index 6f0f794f..5fc1bde1 100644
--- a/lib/ansible/module_utils/ansible_release.py
+++ b/lib/ansible/module_utils/ansible_release.py
@@ -19,6 +19,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-__version__ = '2.14.11'
+__version__ = '2.14.13'
__author__ = 'Ansible, Inc.'
__codename__ = "C'mon Everybody"
diff --git a/lib/ansible/module_utils/common/json.py b/lib/ansible/module_utils/common/json.py
index 727083ca..c4333fc1 100644
--- a/lib/ansible/module_utils/common/json.py
+++ b/lib/ansible/module_utils/common/json.py
@@ -30,7 +30,7 @@ def _preprocess_unsafe_encode(value):
Used in ``AnsibleJSONEncoder.iterencode``
"""
if _is_unsafe(value):
- value = {'__ansible_unsafe': to_text(value, errors='surrogate_or_strict', nonstring='strict')}
+ value = {'__ansible_unsafe': to_text(value._strip_unsafe(), errors='surrogate_or_strict', nonstring='strict')}
elif is_sequence(value):
value = [_preprocess_unsafe_encode(v) for v in value]
elif isinstance(value, Mapping):
@@ -63,7 +63,7 @@ class AnsibleJSONEncoder(json.JSONEncoder):
value = {'__ansible_vault': to_text(o._ciphertext, errors='surrogate_or_strict', nonstring='strict')}
elif getattr(o, '__UNSAFE__', False):
# unsafe object, this will never be triggered, see ``AnsibleJSONEncoder.iterencode``
- value = {'__ansible_unsafe': to_text(o, errors='surrogate_or_strict', nonstring='strict')}
+ value = {'__ansible_unsafe': to_text(o._strip_unsafe(), errors='surrogate_or_strict', nonstring='strict')}
elif isinstance(o, Mapping):
# hostvars and other objects
value = dict(o)
diff --git a/lib/ansible/modules/pip.py b/lib/ansible/modules/pip.py
index a9930ccd..95a5d0d3 100644
--- a/lib/ansible/modules/pip.py
+++ b/lib/ansible/modules/pip.py
@@ -121,6 +121,8 @@ attributes:
platform:
platforms: posix
notes:
+ - Python installations marked externally-managed (as defined by PEP668) cannot be updated by pip versions >= 23.0.1 without the use of
+ a virtual environment or setting the environment variable ``PIP_BREAK_SYSTEM_PACKAGES=1``.
- The virtualenv (U(http://www.virtualenv.org/)) must be
installed on the remote host if the virtualenv parameter is specified and
the virtualenv needs to be created.
diff --git a/lib/ansible/parsing/yaml/dumper.py b/lib/ansible/parsing/yaml/dumper.py
index 8701bb81..bf2c0843 100644
--- a/lib/ansible/parsing/yaml/dumper.py
+++ b/lib/ansible/parsing/yaml/dumper.py
@@ -24,7 +24,7 @@ import yaml
from ansible.module_utils.six import text_type, binary_type
from ansible.module_utils.common.yaml import SafeDumper
from ansible.parsing.yaml.objects import AnsibleUnicode, AnsibleSequence, AnsibleMapping, AnsibleVaultEncryptedUnicode
-from ansible.utils.unsafe_proxy import AnsibleUnsafeText, AnsibleUnsafeBytes, NativeJinjaUnsafeText, NativeJinjaText
+from ansible.utils.unsafe_proxy import AnsibleUnsafeText, AnsibleUnsafeBytes, NativeJinjaUnsafeText, NativeJinjaText, _is_unsafe
from ansible.template import AnsibleUndefined
from ansible.vars.hostvars import HostVars, HostVarsVars
from ansible.vars.manager import VarsWithSources
@@ -47,10 +47,14 @@ def represent_vault_encrypted_unicode(self, data):
def represent_unicode(self, data):
+ if _is_unsafe(data):
+ data = data._strip_unsafe()
return yaml.representer.SafeRepresenter.represent_str(self, text_type(data))
def represent_binary(self, data):
+ if _is_unsafe(data):
+ data = data._strip_unsafe()
return yaml.representer.SafeRepresenter.represent_binary(self, binary_type(data))
diff --git a/lib/ansible/playbook/conditional.py b/lib/ansible/playbook/conditional.py
index fe07358c..d994f8f4 100644
--- a/lib/ansible/playbook/conditional.py
+++ b/lib/ansible/playbook/conditional.py
@@ -26,7 +26,7 @@ from jinja2.compiler import generate
from jinja2.exceptions import UndefinedError
from ansible import constants as C
-from ansible.errors import AnsibleError, AnsibleUndefinedVariable
+from ansible.errors import AnsibleError, AnsibleUndefinedVariable, AnsibleTemplateError
from ansible.module_utils.six import text_type
from ansible.module_utils._text import to_native, to_text
from ansible.playbook.attribute import FieldAttribute
@@ -138,9 +138,10 @@ class Conditional:
if not isinstance(conditional, text_type) or conditional == "":
return conditional
- # update the lookups flag, as the string returned above may now be unsafe
- # and we don't want future templating calls to do unsafe things
- disable_lookups |= hasattr(conditional, '__UNSAFE__')
+ # If the result of the first-pass template render (to resolve inline templates) is marked unsafe,
+ # explicitly fail since the next templating operation would never evaluate
+ if hasattr(conditional, '__UNSAFE__'):
+ raise AnsibleTemplateError('Conditional is marked as unsafe, and cannot be evaluated.')
# First, we do some low-level jinja2 parsing involving the AST format of the
# statement to ensure we don't do anything unsafe (using the disable_lookup flag above)
diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py
index ba35fcf0..a1a1162b 100644
--- a/lib/ansible/playbook/task.py
+++ b/lib/ansible/playbook/task.py
@@ -290,6 +290,30 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
super(Task, self).post_validate(templar)
+ def _post_validate_args(self, attr, value, templar):
+ # smuggle an untemplated copy of the task args for actions that need more control over the templating of their
+ # input (eg, debug's var/msg, assert's "that" conditional expressions)
+ self.untemplated_args = value
+
+ # now recursively template the args dict
+ args = templar.template(value)
+
+ # FIXME: could we just nuke this entirely and/or wrap it up in ModuleArgsParser or something?
+ if '_variable_params' in args:
+ variable_params = args.pop('_variable_params')
+ if isinstance(variable_params, dict):
+ if C.INJECT_FACTS_AS_VARS:
+ display.warning("Using a variable for a task's 'args' is unsafe in some situations "
+ "(see https://docs.ansible.com/ansible/devel/reference_appendices/faq.html#argsplat-unsafe)")
+ variable_params.update(args)
+ args = variable_params
+ else:
+ # if we didn't get a dict, it means there's garbage remaining after k=v parsing, just give up
+ # see https://github.com/ansible/ansible/issues/79862
+ raise AnsibleError(f"invalid or malformed argument: '{variable_params}'")
+
+ return args
+
def _post_validate_loop(self, attr, value, templar):
'''
Override post validation for the loop field, which is templated
diff --git a/lib/ansible/plugins/action/assert.py b/lib/ansible/plugins/action/assert.py
index 7721a6b4..e8ab6a9a 100644
--- a/lib/ansible/plugins/action/assert.py
+++ b/lib/ansible/plugins/action/assert.py
@@ -63,8 +63,29 @@ class ActionModule(ActionBase):
quiet = boolean(self._task.args.get('quiet', False), strict=False)
+ # directly access 'that' via untemplated args from the task so we can intelligently trust embedded
+ # templates and preserve the original inputs/locations for better messaging on assert failures and
+ # errors.
+ # FIXME: even in devel, things like `that: item` don't always work properly (truthy string value
+ # is not really an embedded expression)
+ # we could fix that by doing direct var lookups on the inputs
+ # FIXME: some form of this code should probably be shared between debug, assert, and
+ # Task.post_validate, since they
+ # have a lot of overlapping needs
+ try:
+ thats = self._task.untemplated_args['that']
+ except KeyError:
+ # in the case of "we got our entire args dict from a template", we can just consult the
+ # post-templated dict (the damage has likely already been done for embedded templates anyway)
+ thats = self._task.args['that']
+
+ # FIXME: this is a case where we only want to resolve indirections, NOT recurse containers
+ # (and even then, the leaf-most expression being wrapped is at least suboptimal
+ # (since its expression will be "eaten").
+ if isinstance(thats, str):
+ thats = self._templar.template(thats)
+
# make sure the 'that' items are a list
- thats = self._task.args['that']
if not isinstance(thats, list):
thats = [thats]
diff --git a/lib/ansible/plugins/callback/__init__.py b/lib/ansible/plugins/callback/__init__.py
index d4fc347d..7646d293 100644
--- a/lib/ansible/plugins/callback/__init__.py
+++ b/lib/ansible/plugins/callback/__init__.py
@@ -38,7 +38,7 @@ from ansible.parsing.yaml.objects import AnsibleUnicode
from ansible.plugins import AnsiblePlugin
from ansible.utils.color import stringc
from ansible.utils.display import Display
-from ansible.utils.unsafe_proxy import AnsibleUnsafeText, NativeJinjaUnsafeText
+from ansible.utils.unsafe_proxy import AnsibleUnsafeText, NativeJinjaUnsafeText, _is_unsafe
from ansible.vars.clean import strip_internal_keys, module_response_deepcopy
import yaml
@@ -113,6 +113,8 @@ def _munge_data_for_lossy_yaml(scalar):
def _pretty_represent_str(self, data):
"""Uses block style for multi-line strings"""
+ if _is_unsafe(data):
+ data = data._strip_unsafe()
data = text_type(data)
if _should_use_block(data):
style = '|'
diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py
index 52a2cd10..b7e2c11e 100644
--- a/lib/ansible/plugins/filter/core.py
+++ b/lib/ansible/plugins/filter/core.py
@@ -37,6 +37,7 @@ from ansible.utils.display import Display
from ansible.utils.encrypt import passlib_or_crypt
from ansible.utils.hashing import md5s, checksum_s
from ansible.utils.unicode import unicode_wrap
+from ansible.utils.unsafe_proxy import _is_unsafe
from ansible.utils.vars import merge_hash
display = Display()
@@ -215,6 +216,8 @@ def from_yaml(data):
# The ``text_type`` call here strips any custom
# string wrapper class, so that CSafeLoader can
# read the data
+ if _is_unsafe(data):
+ data = data._strip_unsafe()
return yaml_load(text_type(to_text(data, errors='surrogate_or_strict')))
return data
@@ -224,6 +227,8 @@ def from_yaml_all(data):
# The ``text_type`` call here strips any custom
# string wrapper class, so that CSafeLoader can
# read the data
+ if _is_unsafe(data):
+ data = data._strip_unsafe()
return yaml_load_all(text_type(to_text(data, errors='surrogate_or_strict')))
return data
diff --git a/lib/ansible/plugins/lookup/first_found.py b/lib/ansible/plugins/lookup/first_found.py
index 5b94b103..a882db01 100644
--- a/lib/ansible/plugins/lookup/first_found.py
+++ b/lib/ansible/plugins/lookup/first_found.py
@@ -136,7 +136,6 @@ RETURN = """
elements: path
"""
import os
-import re
from collections.abc import Mapping, Sequence
@@ -147,10 +146,22 @@ from ansible.module_utils.six import string_types
from ansible.plugins.lookup import LookupBase
+def _splitter(value, chars):
+ chars = set(chars)
+ v = ''
+ for c in value:
+ if c in chars:
+ yield v
+ v = ''
+ continue
+ v += c
+ yield v
+
+
def _split_on(terms, spliters=','):
termlist = []
if isinstance(terms, string_types):
- termlist = re.split(r'[%s]' % ''.join(map(re.escape, spliters)), terms)
+ termlist = list(_splitter(terms, spliters))
else:
# added since options will already listify
for t in terms:
diff --git a/lib/ansible/release.py b/lib/ansible/release.py
index 6f0f794f..5fc1bde1 100644
--- a/lib/ansible/release.py
+++ b/lib/ansible/release.py
@@ -19,6 +19,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-__version__ = '2.14.11'
+__version__ = '2.14.13'
__author__ = 'Ansible, Inc.'
__codename__ = "C'mon Everybody"
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py
index baa85ed7..c45cfe35 100644
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -31,7 +31,7 @@ from contextlib import contextmanager
from numbers import Number
from traceback import format_exc
-from jinja2.exceptions import TemplateSyntaxError, UndefinedError
+from jinja2.exceptions import TemplateSyntaxError, UndefinedError, SecurityError
from jinja2.loaders import FileSystemLoader
from jinja2.nativetypes import NativeEnvironment
from jinja2.runtime import Context, StrictUndefined
@@ -55,7 +55,7 @@ from ansible.template.vars import AnsibleJ2Vars
from ansible.utils.display import Display
from ansible.utils.listify import listify_lookup_plugin_terms
from ansible.utils.native_jinja import NativeJinjaText
-from ansible.utils.unsafe_proxy import wrap_var
+from ansible.utils.unsafe_proxy import wrap_var, AnsibleUnsafeText, AnsibleUnsafeBytes, NativeJinjaUnsafeText
display = Display()
@@ -332,10 +332,21 @@ class AnsibleContext(Context):
flag is checked post-templating, and (when set) will result in the
final templated result being wrapped in AnsibleUnsafe.
'''
+ _disallowed_callables = frozenset({
+ AnsibleUnsafeText._strip_unsafe.__qualname__,
+ AnsibleUnsafeBytes._strip_unsafe.__qualname__,
+ NativeJinjaUnsafeText._strip_unsafe.__qualname__,
+ })
+
def __init__(self, *args, **kwargs):
super(AnsibleContext, self).__init__(*args, **kwargs)
self.unsafe = False
+ def call(self, obj, *args, **kwargs):
+ if getattr(obj, '__qualname__', None) in self._disallowed_callables or obj in self._disallowed_callables:
+ raise SecurityError(f"{obj!r} is not safely callable")
+ return super().call(obj, *args, **kwargs)
+
def _is_unsafe(self, val):
'''
Our helper function, which will also recursively check dict and
diff --git a/lib/ansible/utils/unsafe_proxy.py b/lib/ansible/utils/unsafe_proxy.py
index d78ebf6e..683f6e27 100644
--- a/lib/ansible/utils/unsafe_proxy.py
+++ b/lib/ansible/utils/unsafe_proxy.py
@@ -57,7 +57,6 @@ from collections.abc import Mapping, Set
from ansible.module_utils._text import to_bytes, to_text
from ansible.module_utils.common.collections import is_sequence
-from ansible.module_utils.six import string_types, binary_type, text_type
from ansible.utils.native_jinja import NativeJinjaText
@@ -68,16 +67,256 @@ class AnsibleUnsafe(object):
__UNSAFE__ = True
-class AnsibleUnsafeBytes(binary_type, AnsibleUnsafe):
- def decode(self, *args, **kwargs):
- """Wrapper method to ensure type conversions maintain unsafe context"""
- return AnsibleUnsafeText(super(AnsibleUnsafeBytes, self).decode(*args, **kwargs))
+class AnsibleUnsafeBytes(bytes, AnsibleUnsafe):
+ def _strip_unsafe(self):
+ return super().__bytes__()
+ def __reduce__(self, /):
+ return (self.__class__, (self._strip_unsafe(),))
-class AnsibleUnsafeText(text_type, AnsibleUnsafe):
- def encode(self, *args, **kwargs):
- """Wrapper method to ensure type conversions maintain unsafe context"""
- return AnsibleUnsafeBytes(super(AnsibleUnsafeText, self).encode(*args, **kwargs))
+ def __str__(self, /): # pylint: disable=invalid-str-returned
+ return self.decode()
+
+ def __bytes__(self, /): # pylint: disable=invalid-bytes-returned
+ return self
+
+ def __repr__(self, /): # pylint: disable=invalid-repr-returned
+ return AnsibleUnsafeText(super().__repr__())
+
+ def __format__(self, format_spec, /): # pylint: disable=invalid-format-returned
+ return AnsibleUnsafeText(super().__format__(format_spec))
+
+ def __getitem__(self, key, /):
+ if isinstance(key, int):
+ return super().__getitem__(key)
+ return self.__class__(super().__getitem__(key))
+
+ def __reversed__(self, /):
+ return self[::-1]
+
+ def __add__(self, value, /):
+ return self.__class__(super().__add__(value))
+
+ def __radd__(self, value, /):
+ return self.__class__(value.__add__(self))
+
+ def __mul__(self, value, /):
+ return self.__class__(super().__mul__(value))
+
+ __rmul__ = __mul__
+
+ def __mod__(self, value, /):
+ return self.__class__(super().__mod__(value))
+
+ def __rmod__(self, value, /):
+ return self.__class__(super().__rmod__(value))
+
+ def capitalize(self, /):
+ return self.__class__(super().capitalize())
+
+ def center(self, width, fillchar=b' ', /):
+ return self.__class__(super().center(width, fillchar))
+
+ def decode(self, /, encoding='utf-8', errors='strict'):
+ return AnsibleUnsafeText(super().decode(encoding=encoding, errors=errors))
+
+ def removeprefix(self, prefix, /):
+ return self.__class__(super().removeprefix(prefix))
+
+ def removesuffix(self, suffix, /):
+ return self.__class__(super().removesuffix(suffix))
+
+ def expandtabs(self, /, tabsize=8):
+ return self.__class__(super().expandtabs(tabsize))
+
+ def join(self, iterable_of_bytes, /):
+ return self.__class__(super().join(iterable_of_bytes))
+
+ def ljust(self, width, fillchar=b' ', /):
+ return self.__class__(super().ljust(width, fillchar))
+
+ def lower(self, /):
+ return self.__class__(super().lower())
+
+ def lstrip(self, chars=None, /):
+ return self.__class__(super().lstrip(chars))
+
+ def partition(self, sep, /):
+ cls = self.__class__
+ return tuple(cls(e) for e in super().partition(sep))
+
+ def replace(self, old, new, count=-1, /):
+ return self.__class__(super().replace(old, new, count))
+
+ def rjust(self, width, fillchar=b' ', /):
+ return self.__class__(super().rjust(width, fillchar))
+
+ def rpartition(self, sep, /):
+ cls = self.__class__
+ return tuple(cls(e) for e in super().rpartition(sep))
+
+ def rstrip(self, chars=None, /):
+ return self.__class__(super().rstrip(chars))
+
+ def split(self, /, sep=None, maxsplit=-1):
+ cls = self.__class__
+ return [cls(e) for e in super().split(sep=sep, maxsplit=maxsplit)]
+
+ def rsplit(self, /, sep=None, maxsplit=-1):
+ cls = self.__class__
+ return [cls(e) for e in super().rsplit(sep=sep, maxsplit=maxsplit)]
+
+ def splitlines(self, /, keepends=False):
+ cls = self.__class__
+ return [cls(e) for e in super().splitlines(keepends=keepends)]
+
+ def strip(self, chars=None, /):
+ return self.__class__(super().strip(chars))
+
+ def swapcase(self, /):
+ return self.__class__(super().swapcase())
+
+ def title(self, /):
+ return self.__class__(super().title())
+
+ def translate(self, table, /, delete=b''):
+ return self.__class__(super().translate(table, delete=delete))
+
+ def upper(self, /):
+ return self.__class__(super().upper())
+
+ def zfill(self, width, /):
+ return self.__class__(super().zfill(width))
+
+
+class AnsibleUnsafeText(str, AnsibleUnsafe):
+ def _strip_unsafe(self, /):
+ return super().__str__()
+
+ def __reduce__(self, /):
+ return (self.__class__, (self._strip_unsafe(),))
+
+ def __str__(self, /): # pylint: disable=invalid-str-returned
+ return self
+
+ def __repr__(self, /): # pylint: disable=invalid-repr-returned
+ return self.__class__(super().__repr__())
+
+ def __format__(self, format_spec, /): # pylint: disable=invalid-format-returned
+ return self.__class__(super().__format__(format_spec))
+
+ def __getitem__(self, key, /):
+ return self.__class__(super().__getitem__(key))
+
+ def __iter__(self, /):
+ cls = self.__class__
+ return (cls(c) for c in super().__iter__())
+
+ def __reversed__(self, /):
+ return self[::-1]
+
+ def __add__(self, value, /):
+ return self.__class__(super().__add__(value))
+
+ def __radd__(self, value, /):
+ return self.__class__(value.__add__(self))
+
+ def __mul__(self, value, /):
+ return self.__class__(super().__mul__(value))
+
+ __rmul__ = __mul__
+
+ def __mod__(self, value, /):
+ return self.__class__(super().__mod__(value))
+
+ def __rmod__(self, value, /):
+ return self.__class__(super().__rmod__(value))
+
+ def capitalize(self, /):
+ return self.__class__(super().capitalize())
+
+ def casefold(self, /):
+ return self.__class__(super().casefold())
+
+ def center(self, width, fillchar=' ', /):
+ return self.__class__(super().center(width, fillchar))
+
+ def encode(self, /, encoding='utf-8', errors='strict'):
+ return AnsibleUnsafeBytes(super().encode(encoding=encoding, errors=errors))
+
+ def removeprefix(self, prefix, /):
+ return self.__class__(super().removeprefix(prefix))
+
+ def removesuffix(self, suffix, /):
+ return self.__class__(super().removesuffix(suffix))
+
+ def expandtabs(self, /, tabsize=8):
+ return self.__class__(super().expandtabs(tabsize))
+
+ def format(self, /, *args, **kwargs):
+ return self.__class__(super().format(*args, **kwargs))
+
+ def format_map(self, mapping, /):
+ return self.__class__(super().format_map(mapping))
+
+ def join(self, iterable, /):
+ return self.__class__(super().join(iterable))
+
+ def ljust(self, width, fillchar=' ', /):
+ return self.__class__(super().ljust(width, fillchar))
+
+ def lower(self, /):
+ return self.__class__(super().lower())
+
+ def lstrip(self, chars=None, /):
+ return self.__class__(super().lstrip(chars))
+
+ def partition(self, sep, /):
+ cls = self.__class__
+ return tuple(cls(e) for e in super().partition(sep))
+
+ def replace(self, old, new, count=-1, /):
+ return self.__class__(super().replace(old, new, count))
+
+ def rjust(self, width, fillchar=' ', /):
+ return self.__class__(super().rjust(width, fillchar))
+
+ def rpartition(self, sep, /):
+ cls = self.__class__
+ return tuple(cls(e) for e in super().rpartition(sep))
+
+ def rstrip(self, chars=None, /):
+ return self.__class__(super().rstrip(chars))
+
+ def split(self, /, sep=None, maxsplit=-1):
+ cls = self.__class__
+ return [cls(e) for e in super().split(sep=sep, maxsplit=maxsplit)]
+
+ def rsplit(self, /, sep=None, maxsplit=-1):
+ cls = self.__class__
+ return [cls(e) for e in super().rsplit(sep=sep, maxsplit=maxsplit)]
+
+ def splitlines(self, /, keepends=False):
+ cls = self.__class__
+ return [cls(e) for e in super().splitlines(keepends=keepends)]
+
+ def strip(self, chars=None, /):
+ return self.__class__(super().strip(chars))
+
+ def swapcase(self, /):
+ return self.__class__(super().swapcase())
+
+ def title(self, /):
+ return self.__class__(super().title())
+
+ def translate(self, table, /):
+ return self.__class__(super().translate(table))
+
+ def upper(self, /):
+ return self.__class__(super().upper())
+
+ def zfill(self, width, /):
+ return self.__class__(super().zfill(width))
class NativeJinjaUnsafeText(NativeJinjaText, AnsibleUnsafeText):
@@ -112,9 +351,9 @@ def wrap_var(v):
v = _wrap_sequence(v)
elif isinstance(v, NativeJinjaText):
v = NativeJinjaUnsafeText(v)
- elif isinstance(v, binary_type):
+ elif isinstance(v, bytes):
v = AnsibleUnsafeBytes(v)
- elif isinstance(v, text_type):
+ elif isinstance(v, str):
v = AnsibleUnsafeText(v)
return v
@@ -126,3 +365,7 @@ def to_unsafe_bytes(*args, **kwargs):
def to_unsafe_text(*args, **kwargs):
return wrap_var(to_text(*args, **kwargs))
+
+
+def _is_unsafe(obj):
+ return getattr(obj, '__UNSAFE__', False) is True
diff --git a/lib/ansible_core.egg-info/PKG-INFO b/lib/ansible_core.egg-info/PKG-INFO
index e994e044..84fd5acd 100644
--- a/lib/ansible_core.egg-info/PKG-INFO
+++ b/lib/ansible_core.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: ansible-core
-Version: 2.14.11
+Version: 2.14.13
Summary: Radically simple IT automation
Home-page: https://ansible.com/
Author: Ansible, Inc.
diff --git a/lib/ansible_core.egg-info/SOURCES.txt b/lib/ansible_core.egg-info/SOURCES.txt
index f4109a34..50e2a00f 100644
--- a/lib/ansible_core.egg-info/SOURCES.txt
+++ b/lib/ansible_core.egg-info/SOURCES.txt
@@ -1205,9 +1205,12 @@ test/integration/targets/assemble/files/fragment5
test/integration/targets/assemble/meta/main.yml
test/integration/targets/assemble/tasks/main.yml
test/integration/targets/assert/aliases
-test/integration/targets/assert/assert_quiet.out.quiet.stderr
-test/integration/targets/assert/assert_quiet.out.quiet.stdout
+test/integration/targets/assert/assert.out.nested_tmpl.stderr
+test/integration/targets/assert/assert.out.nested_tmpl.stdout
+test/integration/targets/assert/assert.out.quiet.stderr
+test/integration/targets/assert/assert.out.quiet.stdout
test/integration/targets/assert/inventory
+test/integration/targets/assert/nested_tmpl.yml
test/integration/targets/assert/quiet.yml
test/integration/targets/assert/runme.sh
test/integration/targets/async/aliases
@@ -1669,6 +1672,7 @@ test/integration/targets/debug/main.yml
test/integration/targets/debug/main_fqcn.yml
test/integration/targets/debug/nosetfacts.yml
test/integration/targets/debug/runme.sh
+test/integration/targets/debug/unsafe.yml
test/integration/targets/debugger/aliases
test/integration/targets/debugger/inventory
test/integration/targets/debugger/runme.sh
@@ -4102,7 +4106,6 @@ test/support/integration/plugins/module_utils/net_tools/__init__.py
test/support/integration/plugins/module_utils/network/__init__.py
test/support/integration/plugins/module_utils/network/common/__init__.py
test/support/integration/plugins/module_utils/network/common/utils.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 --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml
index f200be18..e0847524 100644
--- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml
+++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml
@@ -13,22 +13,22 @@
in command.stdout_lines
- >-
"Installing 'namespace_1.collection_1:1.0.0' to
- '{{ install_path }}/namespace_1/collection_1'"
+ '" ~ install_path ~ "/namespace_1/collection_1'"
in command.stdout_lines
- >-
'Created collection for namespace_1.collection_1:1.0.0 at
- {{ install_path }}/namespace_1/collection_1'
+ ' ~ install_path ~ '/namespace_1/collection_1'
in command.stdout_lines
- >-
'namespace_1.collection_1:1.0.0 was installed successfully'
in command.stdout_lines
- >-
"Installing 'namespace_2.collection_2:1.0.0' to
- '{{ install_path }}/namespace_2/collection_2'"
+ '" ~ install_path ~ "/namespace_2/collection_2'"
in command.stdout_lines
- >-
'Created collection for namespace_2.collection_2:1.0.0 at
- {{ install_path }}/namespace_2/collection_2'
+ ' ~ install_path ~ '/namespace_2/collection_2'
in command.stdout_lines
- >-
'namespace_2.collection_2:1.0.0 was installed successfully'
@@ -58,22 +58,22 @@
in command.stdout_lines
- >-
"Installing 'namespace_1.collection_1:1.0.0' to
- '{{ install_path }}/namespace_1/collection_1'"
+ '" ~ install_path ~ "/namespace_1/collection_1'"
in command.stdout_lines
- >-
'Created collection for namespace_1.collection_1:1.0.0 at
- {{ install_path }}/namespace_1/collection_1'
+ ' ~ install_path ~ '/namespace_1/collection_1'
in command.stdout_lines
- >-
'namespace_1.collection_1:1.0.0 was installed successfully'
in command.stdout_lines
- >-
"Installing 'namespace_2.collection_2:1.0.0' to
- '{{ install_path }}/namespace_2/collection_2'"
+ '" ~ install_path ~ "/namespace_2/collection_2'"
in command.stdout_lines
- >-
'Created collection for namespace_2.collection_2:1.0.0 at
- {{ install_path }}/namespace_2/collection_2'
+ ' ~ install_path ~ '/namespace_2/collection_2'
in command.stdout_lines
- >-
'namespace_2.collection_2:1.0.0 was installed successfully'
diff --git a/test/integration/targets/ansible-pull/runme.sh b/test/integration/targets/ansible-pull/runme.sh
index 347971a4..582e8099 100755
--- a/test/integration/targets/ansible-pull/runme.sh
+++ b/test/integration/targets/ansible-pull/runme.sh
@@ -84,4 +84,7 @@ pass_tests
ANSIBLE_CONFIG='' ansible-pull -d "${pull_dir}" -U "${repo_dir}" "$@" multi_play_1.yml multi_play_2.yml | tee "${temp_log}"
-pass_tests_multi \ No newline at end of file
+pass_tests_multi
+
+# fail if we try do delete /var/tmp
+ANSIBLE_CONFIG='' ansible-pull -d var/tmp -U "${repo_dir}" --purge "$@"
diff --git a/test/integration/targets/ansible-test-container/runme.py b/test/integration/targets/ansible-test-container/runme.py
index 68712805..8ff48e0d 100755
--- a/test/integration/targets/ansible-test-container/runme.py
+++ b/test/integration/targets/ansible-test-container/runme.py
@@ -1050,9 +1050,16 @@ class ApkBootstrapper(Bootstrapper):
def run(cls) -> None:
"""Run the bootstrapper."""
# The `openssl` package is used to generate hashed passwords.
- packages = ['docker', 'podman', 'openssl']
+ # crun added as podman won't install it as dep if runc is present
+ # but we don't want runc as it fails
+ # The edge `crun` package installed below requires ip6tables, and in
+ # edge, the `iptables` package includes `ip6tables`, but in 3.16 they
+ # are separate.
+ packages = ['docker', 'podman', 'openssl', 'crun', 'ip6tables']
run_command('apk', 'add', *packages)
+ # 3.16 only contains crun 1.4.5, to get 1.9.2 to resolve the run/shm issue, install crun from edge
+ run_command('apk', 'upgrade', '-U', '--repository=http://dl-cdn.alpinelinux.org/alpine/edge/community', 'crun')
run_command('service', 'docker', 'start')
run_command('modprobe', 'tun')
diff --git a/test/integration/targets/ansible-vault/roles/test_vault_embedded/tasks/main.yml b/test/integration/targets/ansible-vault/roles/test_vault_embedded/tasks/main.yml
index eba93896..98ef751b 100644
--- a/test/integration/targets/ansible-vault/roles/test_vault_embedded/tasks/main.yml
+++ b/test/integration/targets/ansible-vault/roles/test_vault_embedded/tasks/main.yml
@@ -2,7 +2,7 @@
- name: Assert that a embedded vault of a string with no newline works
assert:
that:
- - '"{{ vault_encrypted_one_line_var }}" == "Setec Astronomy"'
+ - 'vault_encrypted_one_line_var == "Setec Astronomy"'
- name: Assert that a multi line embedded vault works, including new line
assert:
diff --git a/test/integration/targets/ansible-vault/roles/test_vault_file_encrypted_embedded/tasks/main.yml b/test/integration/targets/ansible-vault/roles/test_vault_file_encrypted_embedded/tasks/main.yml
index e09004a1..107e65cb 100644
--- a/test/integration/targets/ansible-vault/roles/test_vault_file_encrypted_embedded/tasks/main.yml
+++ b/test/integration/targets/ansible-vault/roles/test_vault_file_encrypted_embedded/tasks/main.yml
@@ -2,7 +2,7 @@
- name: Assert that a vault encrypted file with embedded vault of a string with no newline works
assert:
that:
- - '"{{ vault_file_encrypted_with_encrypted_one_line_var }}" == "Setec Astronomy"'
+ - 'vault_file_encrypted_with_encrypted_one_line_var == "Setec Astronomy"'
- name: Assert that a vault encrypted file with multi line embedded vault works, including new line
assert:
diff --git a/test/integration/targets/apt_repository/tasks/apt.yml b/test/integration/targets/apt_repository/tasks/apt.yml
index 0dc25afd..9c15e647 100644
--- a/test/integration/targets/apt_repository/tasks/apt.yml
+++ b/test/integration/targets/apt_repository/tasks/apt.yml
@@ -50,7 +50,7 @@
that:
- 'result.changed'
- 'result.state == "present"'
- - 'result.repo == "{{test_ppa_name}}"'
+ - 'result.repo == test_ppa_name'
- name: 'examine apt cache mtime'
stat: path='/var/cache/apt/pkgcache.bin'
@@ -81,7 +81,7 @@
that:
- 'result.changed'
- 'result.state == "present"'
- - 'result.repo == "{{test_ppa_name}}"'
+ - 'result.repo == test_ppa_name'
- name: 'examine apt cache mtime'
stat: path='/var/cache/apt/pkgcache.bin'
@@ -112,7 +112,7 @@
that:
- 'result.changed'
- 'result.state == "present"'
- - 'result.repo == "{{test_ppa_name}}"'
+ - 'result.repo == test_ppa_name'
- name: 'examine apt cache mtime'
stat: path='/var/cache/apt/pkgcache.bin'
@@ -151,7 +151,7 @@
that:
- 'result.changed'
- 'result.state == "present"'
- - 'result.repo == "{{test_ppa_spec}}"'
+ - 'result.repo == test_ppa_spec'
- result_cache is not changed
- name: 'examine apt cache mtime'
@@ -191,7 +191,7 @@
that:
- 'result.changed'
- 'result.state == "present"'
- - 'result.repo == "{{test_ppa_spec}}"'
+ - 'result.repo == test_ppa_spec'
- name: 'examine source file'
stat: path='/etc/apt/sources.list.d/{{test_ppa_filename}}.list'
diff --git a/test/integration/targets/assert/assert.out.nested_tmpl.stderr b/test/integration/targets/assert/assert.out.nested_tmpl.stderr
new file mode 100644
index 00000000..ea208a41
--- /dev/null
+++ b/test/integration/targets/assert/assert.out.nested_tmpl.stderr
@@ -0,0 +1,4 @@
++ ansible-playbook -i localhost, -c local nested_tmpl.yml
+++ set +x
+[WARNING]: conditional statements should not include jinja2 templating
+delimiters such as {{ }} or {% %}. Found: "{{ foo }}" == "bar"
diff --git a/test/integration/targets/assert/assert.out.nested_tmpl.stdout b/test/integration/targets/assert/assert.out.nested_tmpl.stdout
new file mode 100644
index 00000000..8ca3fb76
--- /dev/null
+++ b/test/integration/targets/assert/assert.out.nested_tmpl.stdout
@@ -0,0 +1,12 @@
+
+PLAY [localhost] ***************************************************************
+
+TASK [assert] ******************************************************************
+ok: [localhost] => {
+ "changed": false,
+ "msg": "All assertions passed"
+}
+
+PLAY RECAP *********************************************************************
+localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+
diff --git a/test/integration/targets/assert/assert_quiet.out.quiet.stderr b/test/integration/targets/assert/assert.out.quiet.stderr
index bd973b04..bd973b04 100644
--- a/test/integration/targets/assert/assert_quiet.out.quiet.stderr
+++ b/test/integration/targets/assert/assert.out.quiet.stderr
diff --git a/test/integration/targets/assert/assert_quiet.out.quiet.stdout b/test/integration/targets/assert/assert.out.quiet.stdout
index b62aac6c..b62aac6c 100644
--- a/test/integration/targets/assert/assert_quiet.out.quiet.stdout
+++ b/test/integration/targets/assert/assert.out.quiet.stdout
diff --git a/test/integration/targets/assert/nested_tmpl.yml b/test/integration/targets/assert/nested_tmpl.yml
new file mode 100644
index 00000000..3da4b1d8
--- /dev/null
+++ b/test/integration/targets/assert/nested_tmpl.yml
@@ -0,0 +1,9 @@
+- hosts: localhost
+ gather_facts: False
+ tasks:
+ - assert:
+ that:
+ - '"{{ foo }}" == "bar"'
+ - foo == "bar"
+ vars:
+ foo: bar
diff --git a/test/integration/targets/assert/quiet.yml b/test/integration/targets/assert/quiet.yml
index 6834712c..1c425cb5 100644
--- a/test/integration/targets/assert/quiet.yml
+++ b/test/integration/targets/assert/quiet.yml
@@ -5,12 +5,12 @@
item_A: yes
tasks:
- assert:
- that: "{{ item }} is defined"
+ that: "item is defined"
quiet: True
with_items:
- item_A
- assert:
- that: "{{ item }} is defined"
+ that: "item is defined"
quiet: False
with_items:
- item_A
diff --git a/test/integration/targets/assert/runme.sh b/test/integration/targets/assert/runme.sh
index ca0a8587..b7907281 100755
--- a/test/integration/targets/assert/runme.sh
+++ b/test/integration/targets/assert/runme.sh
@@ -45,7 +45,7 @@ cleanup() {
fi
}
-BASEFILE=assert_quiet.out
+BASEFILE=assert.out
ORIGFILE="${BASEFILE}"
OUTFILE="${BASEFILE}.new"
@@ -69,3 +69,4 @@ export ANSIBLE_NOCOLOR=1
export ANSIBLE_RETRY_FILES_ENABLED=0
run_test quiet
+run_test nested_tmpl
diff --git a/test/integration/targets/command_shell/tasks/main.yml b/test/integration/targets/command_shell/tasks/main.yml
index 12a944c4..1f4aa5d7 100644
--- a/test/integration/targets/command_shell/tasks/main.yml
+++ b/test/integration/targets/command_shell/tasks/main.yml
@@ -296,7 +296,7 @@
assert:
that:
- shell_result0 is changed
- - shell_result0.cmd == '{{ remote_tmp_dir_test }}/test.sh'
+ - shell_result0.cmd == remote_tmp_dir_test ~ '/test.sh'
- shell_result0.rc == 0
- shell_result0.stderr == ''
- shell_result0.stdout == 'win'
diff --git a/test/integration/targets/copy/tasks/tests.yml b/test/integration/targets/copy/tasks/tests.yml
index 72203563..d6c8e63c 100644
--- a/test/integration/targets/copy/tasks/tests.yml
+++ b/test/integration/targets/copy/tasks/tests.yml
@@ -1176,7 +1176,7 @@
assert:
that:
- "copy_result6.changed"
- - "copy_result6.dest == '{{remote_dir_expanded}}/multiline.txt'"
+ - "copy_result6.dest == remote_dir_expanded ~ '/multiline.txt'"
- "copy_result6.checksum == '9cd0697c6a9ff6689f0afb9136fa62e0b3fee903'"
# test overwriting a file as an unprivileged user (pull request #8624)
@@ -2079,26 +2079,26 @@
assert:
that:
- testcase5 is changed
- - "stat_new_dir_with_chown.stat.uid == {{ ansible_copy_test_user.uid }}"
- - "stat_new_dir_with_chown.stat.gid == {{ ansible_copy_test_group.gid }}"
- - "stat_new_dir_with_chown.stat.pw_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown.stat.gr_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_file1.stat.uid == {{ ansible_copy_test_user.uid }}"
- - "stat_new_dir_with_chown_file1.stat.gid == {{ ansible_copy_test_group.gid }}"
- - "stat_new_dir_with_chown_file1.stat.pw_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_file1.stat.gr_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_subdir.stat.uid == {{ ansible_copy_test_user.uid }}"
- - "stat_new_dir_with_chown_subdir.stat.gid == {{ ansible_copy_test_group.gid }}"
- - "stat_new_dir_with_chown_subdir.stat.pw_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_subdir.stat.gr_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_subdir_file12.stat.uid == {{ ansible_copy_test_user.uid }}"
- - "stat_new_dir_with_chown_subdir_file12.stat.gid == {{ ansible_copy_test_group.gid }}"
- - "stat_new_dir_with_chown_subdir_file12.stat.pw_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_subdir_file12.stat.gr_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_link_file12.stat.uid == {{ ansible_copy_test_user.uid }}"
- - "stat_new_dir_with_chown_link_file12.stat.gid == {{ ansible_copy_test_group.gid }}"
- - "stat_new_dir_with_chown_link_file12.stat.pw_name == '{{ ansible_copy_test_user_name }}'"
- - "stat_new_dir_with_chown_link_file12.stat.gr_name == '{{ ansible_copy_test_user_name }}'"
+ - "stat_new_dir_with_chown.stat.uid == ansible_copy_test_user.uid"
+ - "stat_new_dir_with_chown.stat.gid == ansible_copy_test_group.gid"
+ - "stat_new_dir_with_chown.stat.pw_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown.stat.gr_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_file1.stat.uid == ansible_copy_test_user.uid"
+ - "stat_new_dir_with_chown_file1.stat.gid == ansible_copy_test_group.gid"
+ - "stat_new_dir_with_chown_file1.stat.pw_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_file1.stat.gr_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_subdir.stat.uid == ansible_copy_test_user.uid"
+ - "stat_new_dir_with_chown_subdir.stat.gid == ansible_copy_test_group.gid"
+ - "stat_new_dir_with_chown_subdir.stat.pw_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_subdir.stat.gr_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_subdir_file12.stat.uid == ansible_copy_test_user.uid"
+ - "stat_new_dir_with_chown_subdir_file12.stat.gid == ansible_copy_test_group.gid"
+ - "stat_new_dir_with_chown_subdir_file12.stat.pw_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_subdir_file12.stat.gr_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_link_file12.stat.uid == ansible_copy_test_user.uid"
+ - "stat_new_dir_with_chown_link_file12.stat.gid == ansible_copy_test_group.gid"
+ - "stat_new_dir_with_chown_link_file12.stat.pw_name == ansible_copy_test_user_name"
+ - "stat_new_dir_with_chown_link_file12.stat.gr_name == ansible_copy_test_user_name"
always:
- name: execute - remove the user for test
diff --git a/test/integration/targets/debug/runme.sh b/test/integration/targets/debug/runme.sh
index 5faeb782..dc02859d 100755
--- a/test/integration/targets/debug/runme.sh
+++ b/test/integration/targets/debug/runme.sh
@@ -18,3 +18,5 @@ done
# ensure debug does not set top level vars when looking at ansible_facts
ansible-playbook nosetfacts.yml "$@"
+
+ansible-playbook unsafe.yml "$@"
diff --git a/test/integration/targets/debug/unsafe.yml b/test/integration/targets/debug/unsafe.yml
new file mode 100644
index 00000000..6a78af1a
--- /dev/null
+++ b/test/integration/targets/debug/unsafe.yml
@@ -0,0 +1,13 @@
+- hosts: localhost
+ gather_facts: false
+ vars:
+ unsafe_var: !unsafe undef()|mandatory
+ tasks:
+ - debug:
+ var: '{{ unsafe_var }}'
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is successful
diff --git a/test/integration/targets/dnf/tasks/test_sos_removal.yml b/test/integration/targets/dnf/tasks/test_sos_removal.yml
index 40ceb62b..0d70cf78 100644
--- a/test/integration/targets/dnf/tasks/test_sos_removal.yml
+++ b/test/integration/targets/dnf/tasks/test_sos_removal.yml
@@ -15,5 +15,5 @@
that:
- sos_rm is successful
- sos_rm is changed
- - "'Removed: sos-{{ sos_version }}-{{ sos_release }}' in sos_rm.results[0]"
+ - "'Removed: sos-' ~ sos_version ~ '-' ~ sos_release in sos_rm.results[0]"
- sos_rm.results|length == 1
diff --git a/test/integration/targets/expect/tasks/main.yml b/test/integration/targets/expect/tasks/main.yml
index d6f43f2c..7bf18c5e 100644
--- a/test/integration/targets/expect/tasks/main.yml
+++ b/test/integration/targets/expect/tasks/main.yml
@@ -117,7 +117,7 @@
- name: assert chdir works
assert:
that:
- - "'{{chdir_result.stdout | trim}}' == '{{remote_tmp_dir_real_path.stdout | trim}}'"
+ - "chdir_result.stdout | trim == remote_tmp_dir_real_path.stdout | trim"
- name: test timeout option
expect:
diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml
index 17b0fae6..a5bd68d7 100644
--- a/test/integration/targets/file/tasks/main.yml
+++ b/test/integration/targets/file/tasks/main.yml
@@ -927,7 +927,7 @@
that:
- "file_error3 is failed"
- "file_error3.msg == 'src does not exist'"
- - "file_error3.dest == '{{ remote_tmp_dir_test }}/hard.txt' | expanduser"
+ - "file_error3.dest == remote_tmp_dir_test | expanduser ~ '/hard.txt'"
- "file_error3.src == 'non-existing-file-that-does-not-exist.txt'"
- block:
diff --git a/test/integration/targets/file/tasks/state_link.yml b/test/integration/targets/file/tasks/state_link.yml
index 673fe6fd..6f96cdcb 100644
--- a/test/integration/targets/file/tasks/state_link.yml
+++ b/test/integration/targets/file/tasks/state_link.yml
@@ -199,7 +199,7 @@
- "missing_dst_no_follow_enable_force_use_mode2 is changed"
- "missing_dst_no_follow_enable_force_use_mode3 is not changed"
- "soft3_result['stat'].islnk"
- - "soft3_result['stat'].lnk_target == '{{ user.home }}/nonexistent'"
+ - "soft3_result['stat'].lnk_target == user.home ~ '/nonexistent'"
#
# Test creating a link to a directory https://github.com/ansible/ansible/issues/1369
diff --git a/test/integration/targets/filter_urls/tasks/main.yml b/test/integration/targets/filter_urls/tasks/main.yml
index c062326c..72ed689a 100644
--- a/test/integration/targets/filter_urls/tasks/main.yml
+++ b/test/integration/targets/filter_urls/tasks/main.yml
@@ -19,6 +19,13 @@
- "{'foo': 'bar', 'baz': 'buz'}|urlencode == 'foo=bar&baz=buz'"
- "()|urlencode == ''"
+- name: verify urlencode works for unsafe strings
+ assert:
+ that:
+ - thing|urlencode == 'foo%3Abar'
+ vars:
+ thing: !unsafe foo:bar
+
# Needed (temporarily) due to coverage reports not including the last task.
- assert:
that: true
diff --git a/test/integration/targets/find/tasks/main.yml b/test/integration/targets/find/tasks/main.yml
index 5381a144..89c62b9b 100644
--- a/test/integration/targets/find/tasks/main.yml
+++ b/test/integration/targets/find/tasks/main.yml
@@ -267,7 +267,7 @@
- name: assert we skipped the ogg file
assert:
that:
- - '"{{ remote_tmp_dir_test }}/e/f/g/h/8.ogg" not in find_test3_list'
+ - 'remote_tmp_dir_test ~ "/e/f/g/h/8.ogg" not in find_test3_list'
- name: patterns with regex
find:
@@ -317,7 +317,7 @@
assert:
that:
- result.matched == 1
- - '"{{ remote_tmp_dir_test }}/astest/old.txt" in astest_list'
+ - 'remote_tmp_dir_test ~ "/astest/old.txt" in astest_list'
- name: find files newer than 1 week
find:
@@ -332,7 +332,7 @@
assert:
that:
- result.matched == 1
- - '"{{ remote_tmp_dir_test }}/astest/new.txt" in astest_list'
+ - 'remote_tmp_dir_test ~ "/astest/new.txt" in astest_list'
- name: add some content to the new file
shell: "echo hello world > {{ remote_tmp_dir_test }}/astest/new.txt"
@@ -352,7 +352,7 @@
assert:
that:
- result.matched == 1
- - '"{{ remote_tmp_dir_test }}/astest/new.txt" in astest_list'
+ - 'remote_tmp_dir_test ~ "/astest/new.txt" in astest_list'
- '"checksum" in result.files[0]'
- name: find ANY item with LESS than 5 bytes, also get checksums
@@ -371,6 +371,6 @@
assert:
that:
- result.matched == 2
- - '"{{ remote_tmp_dir_test }}/astest/old.txt" in astest_list'
- - '"{{ remote_tmp_dir_test }}/astest/.hidden.txt" in astest_list'
+ - 'remote_tmp_dir_test ~ "/astest/old.txt" in astest_list'
+ - 'remote_tmp_dir_test ~ "/astest/.hidden.txt" in astest_list'
- '"checksum" in result.files[0]'
diff --git a/test/integration/targets/gathering_facts/test_gathering_facts.yml b/test/integration/targets/gathering_facts/test_gathering_facts.yml
index 47027e87..faa187b7 100644
--- a/test/integration/targets/gathering_facts/test_gathering_facts.yml
+++ b/test/integration/targets/gathering_facts/test_gathering_facts.yml
@@ -433,7 +433,7 @@
- name: Test reading facts from default fact_path
assert:
that:
- - '"{{ ansible_local.testfact.fact_dir }}" == "default"'
+ - 'ansible_local.testfact.fact_dir == "default"'
- hosts: facthost9
tags: [ 'fact_local']
@@ -444,7 +444,7 @@
- name: Test reading facts from custom fact_path
assert:
that:
- - '"{{ ansible_local.testfact.fact_dir }}" == "custom"'
+ - 'ansible_local.testfact.fact_dir == "custom"'
- hosts: facthost20
tags: [ 'fact_facter_ohai' ]
diff --git a/test/integration/targets/git/tasks/depth.yml b/test/integration/targets/git/tasks/depth.yml
index 547f84f7..e0585ca3 100644
--- a/test/integration/targets/git/tasks/depth.yml
+++ b/test/integration/targets/git/tasks/depth.yml
@@ -169,7 +169,7 @@
- name: DEPTH | check update arrived
assert:
that:
- - "{{ a_file.content | b64decode | trim }} == 3"
+ - a_file.content | b64decode | trim == "3"
- git_fetch is changed
- name: DEPTH | clear checkout_dir
diff --git a/test/integration/targets/git/tasks/localmods.yml b/test/integration/targets/git/tasks/localmods.yml
index 09a1326d..0e0cf684 100644
--- a/test/integration/targets/git/tasks/localmods.yml
+++ b/test/integration/targets/git/tasks/localmods.yml
@@ -47,7 +47,7 @@
- name: LOCALMODS | check update arrived
assert:
that:
- - "{{ a_file.content | b64decode | trim }} == 2"
+ - a_file.content | b64decode | trim == "2"
- git_fetch_force is changed
- name: LOCALMODS | clear checkout_dir
@@ -105,7 +105,7 @@
- name: LOCALMODS | check update arrived
assert:
that:
- - "{{ a_file.content | b64decode | trim }} == 2"
+ - a_file.content | b64decode | trim == "2"
- git_fetch_force is changed
- name: LOCALMODS | clear checkout_dir
diff --git a/test/integration/targets/git/tasks/submodules.yml b/test/integration/targets/git/tasks/submodules.yml
index 0b311e79..b6b02490 100644
--- a/test/integration/targets/git/tasks/submodules.yml
+++ b/test/integration/targets/git/tasks/submodules.yml
@@ -32,7 +32,7 @@
- name: SUBMODULES | Ensure submodu1 is at the appropriate commit
assert:
- that: '{{ submodule1.stdout_lines | length }} == 2'
+ that: 'submodule1.stdout_lines | length == 2'
- name: SUBMODULES | clear checkout_dir
file:
@@ -53,7 +53,7 @@
- name: SUBMODULES | Ensure submodule1 is at the appropriate commit
assert:
- that: '{{ submodule1.stdout_lines | length }} == 4'
+ that: 'submodule1.stdout_lines | length == 4'
- name: SUBMODULES | Copy the checkout so we can run several different tests on it
command: 'cp -pr {{ checkout_dir }} {{ checkout_dir }}.bak'
@@ -84,8 +84,8 @@
- name: SUBMODULES | Ensure both submodules are at the appropriate commit
assert:
that:
- - '{{ submodule1.stdout_lines|length }} == 4'
- - '{{ submodule2.stdout_lines|length }} == 2'
+ - 'submodule1.stdout_lines|length == 4'
+ - 'submodule2.stdout_lines|length == 2'
- name: SUBMODULES | Remove checkout dir
@@ -112,7 +112,7 @@
- name: SUBMODULES | Ensure submodule1 is at the appropriate commit
assert:
- that: '{{ submodule1.stdout_lines | length }} == 5'
+ that: 'submodule1.stdout_lines | length == 5'
- name: SUBMODULES | Test that update with recursive found new submodules
@@ -121,7 +121,7 @@
- name: SUBMODULES | Enusre submodule2 is at the appropriate commit
assert:
- that: '{{ submodule2.stdout_lines | length }} == 4'
+ that: 'submodule2.stdout_lines | length == 4'
- name: SUBMODULES | clear checkout_dir
file:
@@ -147,4 +147,4 @@
- name: SUBMODULES | Ensure submodule1 is at the appropriate commit
assert:
- that: '{{ submodule1.stdout_lines | length }} == 4'
+ that: 'submodule1.stdout_lines | length == 4'
diff --git a/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml b/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml
index f1ddc71b..e45331a1 100644
--- a/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml
+++ b/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml
@@ -22,7 +22,7 @@
- name: Check that multiple duplicate lines collapse into a single commands
assert:
that:
- - "{{ result.commands|length }} == 1"
+ - "result.commands|length == 1"
- name: Check that set is correctly prepended
assert:
@@ -58,6 +58,6 @@
- assert:
that:
- - "{{ result.filtered|length }} == 2"
+ - "result.filtered|length == 2"
- debug: msg="END cli/config_check.yaml on connection={{ ansible_connection }}"
diff --git a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml
index 7b2d53a3..316e91c4 100644
--- a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml
+++ b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml
@@ -16,17 +16,17 @@
- name: Assert that the before dicts were correctly generated
assert:
that:
- - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+ - "populate | symmetric_difference(result['before']) |length == 0"
- name: Assert that the correct set of commands were generated
assert:
that:
- - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
+ - "deleted['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that the after dicts were correctly generated
assert:
that:
- - "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}"
+ - "deleted['after'] | symmetric_difference(result['after']) |length == 0"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *deleted
@@ -41,6 +41,6 @@
- name: Assert that the before dicts were correctly generated
assert:
that:
- - "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}"
+ - "deleted['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml
diff --git a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml
index bf968b21..7e0bb53d 100644
--- a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml
+++ b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml
@@ -28,17 +28,17 @@
- name: Assert that before dicts were correctly generated
assert:
- that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}"
+ that: "merged['before'] | symmetric_difference(result['before']) |length == 0"
- name: Assert that correct set of commands were generated
assert:
that:
- - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
+ - "merged['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that after dicts was correctly generated
assert:
that:
- - "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}"
+ - "merged['after'] | symmetric_difference(result['after']) |length == 0"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *merged
@@ -52,7 +52,7 @@
- name: Assert that before dicts were correctly generated
assert:
that:
- - "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
+ - "merged['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml
diff --git a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml
index 8cf038c9..ad13f393 100644
--- a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml
+++ b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml
@@ -19,17 +19,17 @@
- name: Assert that before dicts were correctly generated
assert:
that:
- - "{{ populate_intf | symmetric_difference(result['before']) |length == 0 }}"
+ - "populate_intf | symmetric_difference(result['before']) |length == 0"
- name: Assert that correct commands were generated
assert:
that:
- - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
+ - "overridden['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that after dicts were correctly generated
assert:
that:
- - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}"
+ - "overridden['after'] | symmetric_difference(result['after']) |length == 0"
- name: Overrides all device configuration with provided configurations (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *overridden
@@ -43,7 +43,7 @@
- name: Assert that before dicts were correctly generated
assert:
that:
- - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}"
+ - "overridden['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml
diff --git a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml
index 17acf065..aadc3793 100644
--- a/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml
+++ b/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml
@@ -33,17 +33,17 @@
- name: Assert that correct set of commands were generated
assert:
that:
- - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
+ - "replaced['commands'] | symmetric_difference(result['commands']) |length == 0"
- name: Assert that before dicts are correctly generated
assert:
that:
- - "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
+ - "populate | symmetric_difference(result['before']) |length == 0"
- name: Assert that after dict is correctly generated
assert:
that:
- - "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}"
+ - "replaced['after'] | symmetric_difference(result['after']) |length == 0"
- name: Replace device configurations of listed LLDP interfaces with provided configurarions (IDEMPOTENT)
vyos.vyos.vyos_lldp_interfaces: *replaced
@@ -57,7 +57,7 @@
- name: Assert that before dict is correctly generated
assert:
that:
- - "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}"
+ - "replaced['after'] | symmetric_difference(result['before']) |length == 0"
always:
- include_tasks: _remove_config.yaml
diff --git a/test/integration/targets/include_vars/tasks/main.yml b/test/integration/targets/include_vars/tasks/main.yml
index db15ba3c..6fc4e85a 100644
--- a/test/integration/targets/include_vars/tasks/main.yml
+++ b/test/integration/targets/include_vars/tasks/main.yml
@@ -15,7 +15,7 @@
that:
- "testing == 789"
- "base_dir == 'environments/development'"
- - "{{ included_one_file.ansible_included_var_files | length }} == 1"
+ - "included_one_file.ansible_included_var_files | length == 1"
- "'vars/environments/development/all.yml' in included_one_file.ansible_included_var_files[0]"
- name: include the vars/environments/development/all.yml and save results in all
@@ -51,7 +51,7 @@
assert:
that:
- webapp_version is defined
- - "'file_without_extension' in '{{ include_without_file_extension.ansible_included_var_files | join(' ') }}'"
+ - "'file_without_extension' in include_without_file_extension.ansible_included_var_files | join(' ')"
- name: include every directory in vars
include_vars:
@@ -67,7 +67,7 @@
- "testing == 456"
- "base_dir == 'services'"
- "webapp_containers == 10"
- - "{{ include_every_dir.ansible_included_var_files | length }} == 7"
+ - "include_every_dir.ansible_included_var_files | length == 7"
- "'vars/all/all.yml' in include_every_dir.ansible_included_var_files[0]"
- "'vars/environments/development/all.yml' in include_every_dir.ansible_included_var_files[1]"
- "'vars/environments/development/services/webapp.yml' in include_every_dir.ansible_included_var_files[2]"
@@ -88,9 +88,9 @@
that:
- "testing == 789"
- "base_dir == 'environments/development'"
- - "{{ include_without_webapp.ansible_included_var_files | length }} == 4"
- - "'webapp.yml' not in '{{ include_without_webapp.ansible_included_var_files | join(' ') }}'"
- - "'file_without_extension' not in '{{ include_without_webapp.ansible_included_var_files | join(' ') }}'"
+ - "include_without_webapp.ansible_included_var_files | length == 4"
+ - "'webapp.yml' not in include_without_webapp.ansible_included_var_files | join(' ')"
+ - "'file_without_extension' not in include_without_webapp.ansible_included_var_files | join(' ')"
- name: include only files matching webapp.yml
include_vars:
@@ -104,9 +104,9 @@
- "testing == 101112"
- "base_dir == 'development/services'"
- "webapp_containers == 20"
- - "{{ include_match_webapp.ansible_included_var_files | length }} == 1"
+ - "include_match_webapp.ansible_included_var_files | length == 1"
- "'vars/environments/development/services/webapp.yml' in include_match_webapp.ansible_included_var_files[0]"
- - "'all.yml' not in '{{ include_match_webapp.ansible_included_var_files | join(' ') }}'"
+ - "'all.yml' not in include_match_webapp.ansible_included_var_files | join(' ')"
- name: include only files matching webapp.yml and store results in webapp
include_vars:
@@ -173,10 +173,10 @@
- name: Verify the hash variable
assert:
that:
- - "{{ config | length }} == 3"
+ - "config | length == 3"
- "config.key0 == 0"
- "config.key1 == 0"
- - "{{ config.key2 | length }} == 1"
+ - "config.key2 | length == 1"
- "config.key2.a == 21"
- name: Include the second file to merge the hash variable
@@ -187,10 +187,10 @@
- name: Verify that the hash is merged
assert:
that:
- - "{{ config | length }} == 4"
+ - "config | length == 4"
- "config.key0 == 0"
- "config.key1 == 1"
- - "{{ config.key2 | length }} == 2"
+ - "config.key2 | length == 2"
- "config.key2.a == 21"
- "config.key2.b == 22"
- "config.key3 == 3"
@@ -202,9 +202,9 @@
- name: Verify that the properties from the first file is cleared
assert:
that:
- - "{{ config | length }} == 3"
+ - "config | length == 3"
- "config.key1 == 1"
- - "{{ config.key2 | length }} == 1"
+ - "config.key2 | length == 1"
- "config.key2.b == 22"
- "config.key3 == 3"
diff --git a/test/integration/targets/lookup_ini/test_lookup_properties.yml b/test/integration/targets/lookup_ini/test_lookup_properties.yml
index a6fc0f7d..ed347600 100644
--- a/test/integration/targets/lookup_ini/test_lookup_properties.yml
+++ b/test/integration/targets/lookup_ini/test_lookup_properties.yml
@@ -10,7 +10,7 @@
field_with_space: "{{lookup('ini', 'field.with.space type=properties file=lookup.properties')}}"
- assert:
- that: "{{item}} is defined"
+ that: "item is defined"
with_items: [ 'test1', 'test2', 'test_dot', 'field_with_space' ]
- name: "read ini value"
diff --git a/test/integration/targets/lookup_subelements/tasks/main.yml b/test/integration/targets/lookup_subelements/tasks/main.yml
index 9d93cf20..7885347b 100644
--- a/test/integration/targets/lookup_subelements/tasks/main.yml
+++ b/test/integration/targets/lookup_subelements/tasks/main.yml
@@ -133,7 +133,7 @@
- assert:
that:
- - "'{{ item.0.name }}' != 'carol'"
+ - "item.0.name != 'carol'"
with_subelements:
- "{{ users }}"
- mysql.privs
@@ -220,5 +220,5 @@
- assert:
that:
- - "'{{ user_alice }}' == 'localhost'"
- - "'{{ user_bob }}' == 'db1'"
+ - "user_alice == 'localhost'"
+ - "user_bob == 'db1'"
diff --git a/test/integration/targets/loop_control/inner.yml b/test/integration/targets/loop_control/inner.yml
index 1c286fa4..976f1961 100644
--- a/test/integration/targets/loop_control/inner.yml
+++ b/test/integration/targets/loop_control/inner.yml
@@ -3,7 +3,7 @@
that:
- ansible_loop.index == ansible_loop.index0 + 1
- ansible_loop.revindex == ansible_loop.revindex0 + 1
- - ansible_loop.first == {{ ansible_loop.index == 1 }}
- - ansible_loop.last == {{ ansible_loop.index == ansible_loop.length }}
+ - ansible_loop.first == (ansible_loop.index == 1)
+ - ansible_loop.last == (ansible_loop.index == ansible_loop.length)
- ansible_loop.length == 3
- ansible_loop.allitems|join(',') == 'first,second,third'
diff --git a/test/integration/targets/module_precedence/modules_test_multiple_roles.yml b/test/integration/targets/module_precedence/modules_test_multiple_roles.yml
index f4bd2649..182c2158 100644
--- a/test/integration/targets/module_precedence/modules_test_multiple_roles.yml
+++ b/test/integration/targets/module_precedence/modules_test_multiple_roles.yml
@@ -14,4 +14,4 @@
- assert:
that:
- '"location" in result'
- - 'result["location"] == "{{ expected_location}}"'
+ - 'result["location"] == expected_location'
diff --git a/test/integration/targets/module_precedence/modules_test_multiple_roles_reverse_order.yml b/test/integration/targets/module_precedence/modules_test_multiple_roles_reverse_order.yml
index 5403ae23..ec5619f3 100644
--- a/test/integration/targets/module_precedence/modules_test_multiple_roles_reverse_order.yml
+++ b/test/integration/targets/module_precedence/modules_test_multiple_roles_reverse_order.yml
@@ -13,4 +13,4 @@
- assert:
that:
- '"location" in result'
- - 'result["location"] == "{{ expected_location}}"'
+ - 'result["location"] == expected_location'
diff --git a/test/integration/targets/module_precedence/multiple_roles/bar/tasks/main.yml b/test/integration/targets/module_precedence/multiple_roles/bar/tasks/main.yml
index 52c34020..62b38a7c 100644
--- a/test/integration/targets/module_precedence/multiple_roles/bar/tasks/main.yml
+++ b/test/integration/targets/module_precedence/multiple_roles/bar/tasks/main.yml
@@ -7,4 +7,4 @@
assert:
that:
- '"location" in result'
- - 'result["location"] == "{{ expected_location }}"'
+ - 'result["location"] == expected_location'
diff --git a/test/integration/targets/module_precedence/multiple_roles/foo/tasks/main.yml b/test/integration/targets/module_precedence/multiple_roles/foo/tasks/main.yml
index 52c34020..62b38a7c 100644
--- a/test/integration/targets/module_precedence/multiple_roles/foo/tasks/main.yml
+++ b/test/integration/targets/module_precedence/multiple_roles/foo/tasks/main.yml
@@ -7,4 +7,4 @@
assert:
that:
- '"location" in result'
- - 'result["location"] == "{{ expected_location }}"'
+ - 'result["location"] == expected_location'
diff --git a/test/integration/targets/script/tasks/main.yml b/test/integration/targets/script/tasks/main.yml
index 989513d5..74189f81 100644
--- a/test/integration/targets/script/tasks/main.yml
+++ b/test/integration/targets/script/tasks/main.yml
@@ -198,7 +198,7 @@
assert:
that:
- _check_mode_test2 is skipped
- - '_check_mode_test2.msg == "{{ remote_tmp_dir_test | expanduser }}/afile2.txt exists, matching creates option"'
+ - '_check_mode_test2.msg == remote_tmp_dir_test | expanduser ~ "/afile2.txt exists, matching creates option"'
- name: Remove afile2.txt
file:
@@ -220,7 +220,7 @@
assert:
that:
- _check_mode_test3 is skipped
- - '_check_mode_test3.msg == "{{ remote_tmp_dir_test | expanduser }}/afile2.txt does not exist, matching removes option"'
+ - '_check_mode_test3.msg == remote_tmp_dir_test | expanduser ~ "/afile2.txt does not exist, matching removes option"'
# executable
diff --git a/test/integration/targets/slurp/tasks/main.yml b/test/integration/targets/slurp/tasks/main.yml
index 93985941..f8ebb159 100644
--- a/test/integration/targets/slurp/tasks/main.yml
+++ b/test/integration/targets/slurp/tasks/main.yml
@@ -33,7 +33,7 @@
- 'slurp_existing.encoding == "base64"'
- 'slurp_existing is not changed'
- 'slurp_existing is not failed'
- - '"{{ slurp_existing.content | b64decode }}" == "We are at the café"'
+ - 'slurp_existing.content | b64decode == "We are at the café"'
- name: Create a binary file to test with
copy:
diff --git a/test/integration/targets/subversion/aliases b/test/integration/targets/subversion/aliases
index 23ada3cc..3cc41e4c 100644
--- a/test/integration/targets/subversion/aliases
+++ b/test/integration/targets/subversion/aliases
@@ -1,4 +1,3 @@
-setup/always/setup_passlib
shippable/posix/group2
skip/osx
skip/macos
diff --git a/test/integration/targets/subversion/roles/subversion/defaults/main.yml b/test/integration/targets/subversion/roles/subversion/defaults/main.yml
index e647d598..02ecd1ea 100644
--- a/test/integration/targets/subversion/roles/subversion/defaults/main.yml
+++ b/test/integration/targets/subversion/roles/subversion/defaults/main.yml
@@ -3,8 +3,7 @@ apache_port: 11386 # cannot use 80 as httptester overrides this
subversion_test_dir: /tmp/ansible-svn-test-dir
subversion_server_dir: /tmp/ansible-svn # cannot use a path in the home dir without userdir or granting exec permission to the apache user
subversion_repo_name: ansible-test-repo
-subversion_repo_url: http://127.0.0.1:{{ apache_port }}/svn/{{ subversion_repo_name }}
-subversion_repo_auth_url: http://127.0.0.1:{{ apache_port }}/svnauth/{{ subversion_repo_name }}
+subversion_repo_url: https://localhost:{{ apache_port }}/svn/{{ subversion_repo_name }} # svn can't verify TLS certificates against IP addresses
+subversion_repo_auth_url: https://localhost:{{ apache_port }}/svnauth/{{ subversion_repo_name }}
subversion_username: subsvn_user'''
subversion_password: Password123!
-subversion_external_repo_url: https://github.com/ansible/ansible.github.com # GitHub serves SVN
diff --git a/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml b/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml
index 9be43b4c..f86cf59b 100644
--- a/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml
+++ b/test/integration/targets/subversion/roles/subversion/tasks/cleanup.yml
@@ -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 --git a/test/integration/targets/subversion/roles/subversion/tasks/setup.yml b/test/integration/targets/subversion/roles/subversion/tasks/setup.yml
index 3cf5af56..880c295c 100644
--- a/test/integration/targets/subversion/roles/subversion/tasks/setup.yml
+++ b/test/integration/targets/subversion/roles/subversion/tasks/setup.yml
@@ -33,6 +33,60 @@
include_tasks: setup_selinux.yml
when: ansible_selinux.status == "enabled"
+- name: Generate CA and TLS certificates via trustme
+ vars:
+ venv_path: >-
+ {{ subversion_server_dir }}/.venv
+ venv_python: >-
+ {{ subversion_server_dir }}/.venv/bin/python
+ block:
+ - name: trustme -- provision a venv
+ command: >-
+ {{ ansible_python_interpreter }}
+ -{% if ansible_python.version.major != 2 %}I{% endif %}m
+ {% if ansible_python.version.major != 2 %}venv{%
+ else %}virtualenv{% endif %}
+
+ {{ venv_path }}
+ - name: trustme -- upgrade pip in venv | RHEL 7.9 & 8.8+py36
+ when: >- # these don't know how to notice `cryptography` wheels
+ ansible_distribution == 'RedHat'
+ and ansible_distribution_major_version | int < 9
+ pip:
+ name: pip
+ state: latest
+ virtualenv: >-
+ {{ venv_path }}
+ - name: trustme -- install tool
+ pip:
+ name: trustme
+ virtualenv: >-
+ {{ venv_path }}
+ - name: trustme -- generate CA and TLS certs
+ command:
+ argv:
+ - >-
+ {{ venv_python }}
+ - -{%- if ansible_python.version.major != 2 -%}I{%- endif -%}m
+ - trustme
+ - --dir={{ subversion_server_dir }}
+
+- name: symlink trustme certificates into apache config dir - Red Hat
+ when: ansible_os_family in ['RedHat']
+ # when: ansible_distribution in ['Fedora', 'RedHat']
+ file:
+ src: /tmp/ansible-svn/server.{{ item.trustme_filetype }}
+ dest: /etc/pki/tls/{{ item.apache_target_path }}
+ state: link
+ force: yes # Othewise Apache on CentOS 7 uses its own fake certificate
+ loop:
+ - apache_target_path: certs/localhost.crt
+ trustme_filetype: pem
+ - apache_target_path: certs/server-chain.crt
+ trustme_filetype: pem
+ - apache_target_path: private/localhost.key
+ trustme_filetype: key
+
- name: template out configuration file
template:
src: subversion.conf.j2
@@ -45,11 +99,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 +112,22 @@
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']
+
+- lineinfile:
+ dest: >-
+ {{ ansible_env.HOME }}/.subversion/servers
+ regexp: >-
+ ^#\s*ssl-authority-files\s*=\s*
+ line: >-
+ ssl-authority-files = {{ subversion_server_dir }}/client.pem
+ insertafter: >-
+ ^\[global\]
+ state: present
diff --git a/test/integration/targets/subversion/roles/subversion/tasks/tests.yml b/test/integration/targets/subversion/roles/subversion/tasks/tests.yml
index b8f85d95..70737a19 100644
--- a/test/integration/targets/subversion/roles/subversion/tasks/tests.yml
+++ b/test/integration/targets/subversion/roles/subversion/tasks/tests.yml
@@ -18,10 +18,11 @@
# checks out every branch so using a small repo
-- name: initial checkout
+- name: initial checkout with validate_certs=true
subversion:
repo: '{{ subversion_repo_url }}'
dest: '{{ subversion_test_dir }}/svn'
+ validate_certs: yes
register: subverted
- name: check if dir was checked out
@@ -130,16 +131,27 @@
- "export_branches.stat.isdir"
- "subverted4.changed"
-- name: clone a small external repo with validate_certs=true
+- name: unconfigure client-side TLS trust
+ block:
+ - name: remove TLS CA chain file path from the SVN config
+ lineinfile:
+ dest: >-
+ {{ ansible_env.HOME }}/.subversion/servers
+ regexp: >-
+ ^(?:#)?\s*ssl-authority-files\s*=\s*
+ state: absent
+ - name: drop the client TLS CA chain file
+ file:
+ path: >-
+ {{ subversion_server_dir }}/client.pem
+ state: absent
+
+- name: >-
+ clone a HTTPS-accessible repo with validate_certs=false
+ and untrusted CA over TLS
subversion:
- repo: "{{ subversion_external_repo_url }}"
- dest: "{{ subversion_test_dir }}/svn-external1"
- validate_certs: yes
-
-- name: clone a small external repo with validate_certs=false
- subversion:
- repo: "{{ subversion_external_repo_url }}"
- dest: "{{ subversion_test_dir }}/svn-external2"
+ repo: '{{ subversion_repo_url }}'
+ dest: '{{ subversion_test_dir }}/svn-untrusted-tls'
validate_certs: no
# TBA: test for additional options or URL variants welcome
diff --git a/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2 b/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2
index 86f40707..133c70c6 100644
--- a/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2
+++ b/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2
@@ -19,6 +19,7 @@ LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
+Include mods-available/ssl.load
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
IncludeOptional conf-enabled/*.conf
@@ -32,6 +33,7 @@ IncludeOptional sites-enabled/*conf
{% elif ansible_os_family == "FreeBSD" %}
Include /usr/local/etc/apache24/httpd.conf
+LoadModule ssl_module libexec/apache24/mod_ssl.so
LoadModule dav_module libexec/apache24/mod_dav.so
LoadModule dav_svn_module libexec/apache24/mod_dav_svn.so
LoadModule authz_svn_module libexec/apache24/mod_authz_svn.so
@@ -48,7 +50,14 @@ Include /etc/httpd/conf/httpd.conf
{% endif %}
PidFile {{ subversion_server_dir }}/apache.pid
-Listen 127.0.0.1:{{ apache_port }}
+Listen 127.0.0.1:{{ apache_port }} https
+{% if ansible_distribution not in ["Alpine", "CentOS", "Fedora", "openSUSE Leap", "Ubuntu"] %}
+Listen [::1]:{{ apache_port }} https
+{% endif %}
+SSLEngine on
+SSLCertificateFile {{ subversion_server_dir }}/server.pem
+SSLCertificateKeyFile {{ subversion_server_dir }}/server.key
+SSLCertificateChainFile {{ subversion_server_dir }}/server.pem
ErrorLog {{ subversion_server_dir }}/apache2-error.log
<Location /svn>
diff --git a/test/integration/targets/subversion/vars/Alpine.yml b/test/integration/targets/subversion/vars/Alpine.yml
index ce071fdd..71077b12 100644
--- a/test/integration/targets/subversion/vars/Alpine.yml
+++ b/test/integration/targets/subversion/vars/Alpine.yml
@@ -3,5 +3,8 @@ subversion_packages:
- subversion
- mod_dav_svn
- apache2-webdav
+- apache2-utils
+- apache2-ctl
+- apache2-ssl
apache_user: apache
apache_group: apache
diff --git a/test/integration/targets/subversion/vars/RedHat.yml b/test/integration/targets/subversion/vars/RedHat.yml
index 3e3f9109..a3298318 100644
--- a/test/integration/targets/subversion/vars/RedHat.yml
+++ b/test/integration/targets/subversion/vars/RedHat.yml
@@ -1,6 +1,7 @@
---
subversion_packages:
- mod_dav_svn
+- mod_ssl
- subversion
upgrade_packages:
# prevent sqlite from being out-of-sync with the version subversion was compiled with
diff --git a/test/integration/targets/subversion/vars/Ubuntu-20.yml b/test/integration/targets/subversion/vars/Ubuntu-20.yml
index dfe131b0..bfd880fd 100644
--- a/test/integration/targets/subversion/vars/Ubuntu-20.yml
+++ b/test/integration/targets/subversion/vars/Ubuntu-20.yml
@@ -1,5 +1,7 @@
---
subversion_packages:
+- apache2 # /usr/sbin/apachectl
+- apache2-utils # htpasswd
- subversion
- libapache2-mod-svn
apache_user: www-data
diff --git a/test/integration/targets/template/tasks/main.yml b/test/integration/targets/template/tasks/main.yml
index c0d2e11a..3c91734b 100644
--- a/test/integration/targets/template/tasks/main.yml
+++ b/test/integration/targets/template/tasks/main.yml
@@ -357,7 +357,7 @@
- assert:
that:
- "\"foo t'e~m\\plated\" in unusual_results.stdout_lines"
- - "{{unusual_results.stdout_lines| length}} == 1"
+ - "unusual_results.stdout_lines| length == 1"
- name: check that the unusual filename can be checked for changes
template:
diff --git a/test/integration/targets/unarchive/tasks/test_missing_binaries.yml b/test/integration/targets/unarchive/tasks/test_missing_binaries.yml
index 58d38f4f..49f862b4 100644
--- a/test/integration/targets/unarchive/tasks/test_missing_binaries.yml
+++ b/test/integration/targets/unarchive/tasks/test_missing_binaries.yml
@@ -66,7 +66,7 @@
- zip_success.changed
# Verify that file list is generated
- "'files' in zip_success"
- - "{{zip_success['files']| length}} == 3"
+ - "zip_success['files']| length == 3"
- "'foo-unarchive.txt' in zip_success['files']"
- "'foo-unarchive-777.txt' in zip_success['files']"
- "'FOO-UNAR.TXT' in zip_success['files']"
diff --git a/test/integration/targets/unarchive/tasks/test_mode.yml b/test/integration/targets/unarchive/tasks/test_mode.yml
index c69e3bd2..06fbc7b8 100644
--- a/test/integration/targets/unarchive/tasks/test_mode.yml
+++ b/test/integration/targets/unarchive/tasks/test_mode.yml
@@ -24,7 +24,7 @@
- "unarchive06_stat.stat.mode == '0600'"
# Verify that file list is generated
- "'files' in unarchive06"
- - "{{unarchive06['files']| length}} == 1"
+ - "unarchive06['files']| length == 1"
- "'foo-unarchive.txt' in unarchive06['files']"
- name: remove our tar.gz unarchive destination
@@ -74,7 +74,7 @@
- "unarchive07.changed == false"
# Verify that file list is generated
- "'files' in unarchive07"
- - "{{unarchive07['files']| length}} == 1"
+ - "unarchive07['files']| length == 1"
- "'foo-unarchive.txt' in unarchive07['files']"
- name: remove our tar.gz unarchive destination
@@ -108,7 +108,7 @@
- "unarchive08_stat.stat.mode == '0601'"
# Verify that file list is generated
- "'files' in unarchive08"
- - "{{unarchive08['files']| length}} == 3"
+ - "unarchive08['files']| length == 3"
- "'foo-unarchive.txt' in unarchive08['files']"
- "'foo-unarchive-777.txt' in unarchive08['files']"
- "'FOO-UNAR.TXT' in unarchive08['files']"
@@ -140,7 +140,7 @@
- "unarchive08_stat.stat.mode == '0601'"
# Verify that file list is generated
- "'files' in unarchive08"
- - "{{unarchive08['files']| length}} == 3"
+ - "unarchive08['files']| length == 3"
- "'foo-unarchive.txt' in unarchive08['files']"
- "'foo-unarchive-777.txt' in unarchive08['files']"
- "'FOO-UNAR.TXT' in unarchive08['files']"
diff --git a/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml b/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml
index 8ee1db49..9f45e4c9 100644
--- a/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml
+++ b/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml
@@ -40,7 +40,7 @@
- unarchive10 is changed
# Verify that file list is generated
- "'files' in unarchive10"
- - "{{unarchive10['files']| length}} == 1"
+ - "unarchive10['files']| length == 1"
- "'foo-unarchive.txt' in unarchive10['files']"
- archive_path.stat.exists
diff --git a/test/integration/targets/unarchive/tasks/test_zip.yml b/test/integration/targets/unarchive/tasks/test_zip.yml
index cf03946f..0fc5dc9c 100644
--- a/test/integration/targets/unarchive/tasks/test_zip.yml
+++ b/test/integration/targets/unarchive/tasks/test_zip.yml
@@ -17,7 +17,7 @@
- "unarchive03.changed == true"
# Verify that file list is generated
- "'files' in unarchive03"
- - "{{unarchive03['files']| length}} == 3"
+ - "unarchive03['files']| length == 3"
- "'foo-unarchive.txt' in unarchive03['files']"
- "'foo-unarchive-777.txt' in unarchive03['files']"
- "'FOO-UNAR.TXT' in unarchive03['files']"
diff --git a/test/integration/targets/wait_for/tasks/main.yml b/test/integration/targets/wait_for/tasks/main.yml
index f71ddbda..f81fd0f2 100644
--- a/test/integration/targets/wait_for/tasks/main.yml
+++ b/test/integration/targets/wait_for/tasks/main.yml
@@ -40,7 +40,7 @@
assert:
that:
- waitfor is successful
- - waitfor.path == "{{ remote_tmp_dir | expanduser }}/wait_for_file"
+ - waitfor.path == remote_tmp_dir | expanduser ~ "/wait_for_file"
- waitfor.elapsed >= 2
- waitfor.elapsed <= 15
@@ -58,7 +58,7 @@
assert:
that:
- waitfor is successful
- - waitfor.path == "{{ remote_tmp_dir | expanduser }}/wait_for_file"
+ - waitfor.path == remote_tmp_dir | expanduser ~ "/wait_for_file"
- waitfor.elapsed >= 2
- waitfor.elapsed <= 15
@@ -156,7 +156,7 @@
that:
- waitfor is successful
- waitfor is not changed
- - "waitfor.port == {{ http_port }}"
+ - "waitfor.port == http_port"
- name: install psutil using pip (non-Linux only)
pip:
@@ -184,7 +184,7 @@
that:
- waitfor is successful
- waitfor is not changed
- - "waitfor.port == {{ http_port }}"
+ - "waitfor.port == http_port"
- name: test wait_for with delay
wait_for:
diff --git a/test/lib/ansible_test/_data/completion/remote.txt b/test/lib/ansible_test/_data/completion/remote.txt
index 4e607b7a..9cb8dee8 100644
--- a/test/lib/ansible_test/_data/completion/remote.txt
+++ b/test/lib/ansible_test/_data/completion/remote.txt
@@ -3,7 +3,7 @@ alpine become=doas_sudo provider=aws arch=x86_64
fedora/36 python=3.10 become=sudo provider=aws arch=x86_64
fedora become=sudo provider=aws arch=x86_64
freebsd/12.4 python=3.9 python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
-freebsd/13.1 python=3.8,3.7,3.9,3.10 python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
+freebsd/13.2 python=3.8,3.7,3.9,3.10 python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
freebsd python_dir=/usr/local/bin become=su_sudo provider=aws arch=x86_64
macos/12.0 python=3.10 python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64
macos python_dir=/usr/local/bin become=sudo provider=parallels arch=x86_64
diff --git a/test/lib/ansible_test/_data/completion/windows.txt b/test/lib/ansible_test/_data/completion/windows.txt
index 767c36cb..92b0d086 100644
--- a/test/lib/ansible_test/_data/completion/windows.txt
+++ b/test/lib/ansible_test/_data/completion/windows.txt
@@ -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 --git a/test/lib/ansible_test/_internal/cgroup.py b/test/lib/ansible_test/_internal/cgroup.py
index a08513a5..c9da2465 100644
--- a/test/lib/ansible_test/_internal/cgroup.py
+++ b/test/lib/ansible_test/_internal/cgroup.py
@@ -44,7 +44,7 @@ class CGroupEntry:
@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 --git a/test/support/integration/plugins/modules/htpasswd.py b/test/support/integration/plugins/modules/htpasswd.py
deleted file mode 100644
index 2c55a6bc..00000000
--- a/test/support/integration/plugins/modules/htpasswd.py
+++ /dev/null
@@ -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 --git a/test/units/parsing/yaml/test_dumper.py b/test/units/parsing/yaml/test_dumper.py
index 5fbc139b..cbf5b456 100644
--- a/test/units/parsing/yaml/test_dumper.py
+++ b/test/units/parsing/yaml/test_dumper.py
@@ -29,7 +29,6 @@ from ansible.parsing.yaml import dumper, objects
from ansible.parsing.yaml.loader import AnsibleLoader
from ansible.module_utils.six import PY2
from ansible.template import AnsibleUndefined
-from ansible.utils.unsafe_proxy import AnsibleUnsafeText, AnsibleUnsafeBytes
from units.mock.yaml_helper import YamlTestUtils
from units.mock.vault_helper import TextVaultSecret
@@ -69,8 +68,7 @@ class TestAnsibleDumper(unittest.TestCase, YamlTestUtils):
def test_bytes(self):
b_text = u'tréma'.encode('utf-8')
- unsafe_object = AnsibleUnsafeBytes(b_text)
- yaml_out = self._dump_string(unsafe_object, dumper=self.dumper)
+ yaml_out = self._dump_string(b_text, dumper=self.dumper)
stream = self._build_stream(yaml_out)
loader = self._loader(stream)
@@ -97,8 +95,7 @@ class TestAnsibleDumper(unittest.TestCase, YamlTestUtils):
def test_unicode(self):
u_text = u'nöel'
- unsafe_object = AnsibleUnsafeText(u_text)
- yaml_out = self._dump_string(unsafe_object, dumper=self.dumper)
+ yaml_out = self._dump_string(u_text, dumper=self.dumper)
stream = self._build_stream(yaml_out)
loader = self._loader(stream)