summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Martz <matt@sivel.net>2020-09-08 08:52:27 -0500
committerGitHub <noreply@github.com>2020-09-08 08:52:27 -0500
commit61f6aa55b6366b99d3ad3ed0701fdf44159474c5 (patch)
treec44050af23321c3f6e77bf22868bb391feca7670
parentb615789fcc8d2ddd831ef04c89163300c3bc1a0b (diff)
downloadansible-61f6aa55b6366b99d3ad3ed0701fdf44159474c5.tar.gz
Add semver support to the jinja2 version test (#71600)
-rw-r--r--changelogs/fragments/version-test-semver.yml2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_tests.rst8
-rw-r--r--lib/ansible/plugins/test/core.py33
-rw-r--r--test/integration/targets/test_core/tasks/main.yml22
4 files changed, 58 insertions, 7 deletions
diff --git a/changelogs/fragments/version-test-semver.yml b/changelogs/fragments/version-test-semver.yml
new file mode 100644
index 0000000000..4526b6513c
--- /dev/null
+++ b/changelogs/fragments/version-test-semver.yml
@@ -0,0 +1,2 @@
+minor_changes:
+- version test - Add semantic version functionality
diff --git a/docs/docsite/rst/user_guide/playbooks_tests.rst b/docs/docsite/rst/user_guide/playbooks_tests.rst
index 0a1aa8d91f..419305ee48 100644
--- a/docs/docsite/rst/user_guide/playbooks_tests.rst
+++ b/docs/docsite/rst/user_guide/playbooks_tests.rst
@@ -153,6 +153,14 @@ This test also accepts a 3rd parameter, ``strict`` which defines if strict versi
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
+As of Ansible 2.11 the ``version`` test accepts a ``version_type`` parameter which is mutually exclusive with ``strict``, and accepts the following values::
+
+ loose, strict, semver, semantic
+
+Using ``version_type`` to compare a semantic version would be achieved like the following::
+
+ {{ sample_semver_var is version('2.0.0-rc.1+build.123', 'lt', version_type='semver') }}
+
When using ``version`` in a playbook or role, don't use ``{{ }}`` as described in the `FAQ <https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#when-should-i-use-also-how-to-interpolate-variables-or-dynamic-variable-names>`_::
vars:
diff --git a/lib/ansible/plugins/test/core.py b/lib/ansible/plugins/test/core.py
index 40733a1402..4b3995dd16 100644
--- a/lib/ansible/plugins/test/core.py
+++ b/lib/ansible/plugins/test/core.py
@@ -24,10 +24,11 @@ import operator as py_operator
from distutils.version import LooseVersion, StrictVersion
from ansible import errors
-from ansible.module_utils._text import to_text
+from ansible.module_utils._text import to_native, to_text
from ansible.module_utils.common._collections_compat import MutableMapping, MutableSequence
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.utils.display import Display
+from ansible.utils.version import SemanticVersion
display = Display()
@@ -146,7 +147,7 @@ def search(value, pattern='', ignorecase=False, multiline=False):
return regex(value, pattern, ignorecase, multiline, 'search')
-def version_compare(value, version, operator='eq', strict=False):
+def version_compare(value, version, operator='eq', strict=None, version_type=None):
''' Perform a version comparison on a value '''
op_map = {
'==': 'eq', '=': 'eq', 'eq': 'eq',
@@ -157,21 +158,39 @@ def version_compare(value, version, operator='eq', strict=False):
'!=': 'ne', '<>': 'ne', 'ne': 'ne'
}
+ type_map = {
+ 'loose': LooseVersion,
+ 'strict': StrictVersion,
+ 'semver': SemanticVersion,
+ 'semantic': SemanticVersion,
+ }
+
+ if strict is not None and version_type is not None:
+ raise errors.AnsibleFilterError("Cannot specify both 'strict' and 'version_type'")
+
+ Version = LooseVersion
if strict:
Version = StrictVersion
- else:
- Version = LooseVersion
+ elif version_type:
+ try:
+ Version = type_map[version_type]
+ except KeyError:
+ raise errors.AnsibleFilterError(
+ "Invalid version type (%s). Must be one of %s" % (version_type, ', '.join(map(repr, type_map)))
+ )
if operator in op_map:
operator = op_map[operator]
else:
- raise errors.AnsibleFilterError('Invalid operator type')
+ raise errors.AnsibleFilterError(
+ 'Invalid operator type (%s). Must be one of %s' % (operator, ', '.join(map(repr, op_map)))
+ )
try:
method = getattr(py_operator, operator)
- return method(Version(str(value)), Version(str(version)))
+ return method(Version(to_text(value)), Version(to_text(version)))
except Exception as e:
- raise errors.AnsibleFilterError('Version comparison: %s' % e)
+ raise errors.AnsibleFilterError('Version comparison failed: %s' % to_native(e))
def truthy(value, convert_bool=False):
diff --git a/test/integration/targets/test_core/tasks/main.yml b/test/integration/targets/test_core/tasks/main.yml
index 50c435815b..4f8441e17e 100644
--- a/test/integration/targets/test_core/tasks/main.yml
+++ b/test/integration/targets/test_core/tasks/main.yml
@@ -217,6 +217,24 @@
ignore_errors: yes
register: version_bad_value
+- name: Try version with both strict and version_type
+ debug:
+ msg: "{{ '1.0' is version('1.0', strict=False, version_type='loose') }}"
+ ignore_errors: yes
+ register: version_strict_version_type
+
+- name: Try version with bad version_type
+ debug:
+ msg: "{{ '1.0' is version('1.0', version_type='boom') }}"
+ ignore_errors: yes
+ register: version_bad_version_type
+
+- name: Try version with bad semver
+ debug:
+ msg: "{{ 'nope' is version('nopenope', version_type='semver') }}"
+ ignore_errors: yes
+ register: version_bad_semver
+
- name: Assert version tests work
assert:
that:
@@ -234,8 +252,12 @@
- "'2.0' is version('1.0', '>', true)"
- "'1.0' is version('1.0', '<=', true)"
- "'1.0' is version('1.0', '>=', true)"
+ - "'1.2.3' is version('2.0.0', 'lt', version_type='semver')"
- version_bad_operator is failed
- version_bad_value is failed
+ - version_strict_version_type is failed
+ - version_bad_version_type is failed
+ - version_bad_semver is failed
- name: Assert any tests work
assert: