From 086b4e4fb8566fc493ca1c5488115e0d94ec17eb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:19:14 +0200 Subject: [PATCH] [PR #8595/8990f97b backport][stable-9] New Plugins #8594 (#8618) New Plugins #8594 (#8595) * Implement #8594 * Fix lint and BOTMETA entries. * Fix BOTMETA * Consolidate argument check, code simplification, and formatting. Remove test vars. * Fix lint. * retrigger checks * Update plugins/plugin_utils/ansible_type.py Co-authored-by: Felix Fontein * Update plugins/test/ansible_type.py Co-authored-by: Felix Fontein --------- Co-authored-by: Felix Fontein (cherry picked from commit 8990f97b45e69ce9709e03b5adb13ef3d968b3f5) Co-authored-by: Vladimir Botka --- .github/BOTMETA.yml | 6 + plugins/filter/reveal_ansible_type.py | 134 ++++++++++ plugins/plugin_utils/ansible_type.py | 47 ++++ plugins/test/ansible_type.py | 203 ++++++++++++++ .../filter_reveal_ansible_type/aliases | 5 + .../filter_reveal_ansible_type/tasks/main.yml | 7 + .../tasks/tasks.yml | 185 +++++++++++++ .../targets/test_ansible_type/aliases | 5 + .../targets/test_ansible_type/tasks/main.yml | 7 + .../targets/test_ansible_type/tasks/tasks.yml | 248 ++++++++++++++++++ 10 files changed, 847 insertions(+) create mode 100644 plugins/filter/reveal_ansible_type.py create mode 100644 plugins/plugin_utils/ansible_type.py create mode 100644 plugins/test/ansible_type.py create mode 100644 tests/integration/targets/filter_reveal_ansible_type/aliases create mode 100644 tests/integration/targets/filter_reveal_ansible_type/tasks/main.yml create mode 100644 tests/integration/targets/filter_reveal_ansible_type/tasks/tasks.yml create mode 100644 tests/integration/targets/test_ansible_type/aliases create mode 100644 tests/integration/targets/test_ansible_type/tasks/main.yml create mode 100644 tests/integration/targets/test_ansible_type/tasks/tasks.yml diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 62b094da75..9eb521018f 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -176,6 +176,8 @@ files: maintainers: vbotka $filters/replace_keys.py: maintainers: vbotka + $filters/reveal_ansible_type.py: + maintainers: vbotka $filters/time.py: maintainers: resmo $filters/to_days.yml: @@ -1425,12 +1427,16 @@ files: ignore: matze labels: zypper maintainers: $team_suse + $plugin_utils/ansible_type.py: + maintainers: vbotka $plugin_utils/keys_filter.py: maintainers: vbotka $plugin_utils/unsafe.py: maintainers: felixfontein $tests/a_module.py: maintainers: felixfontein + $tests/ansible_type.py: + maintainers: vbotka $tests/fqdn_valid.py: maintainers: vbotka ######################### diff --git a/plugins/filter/reveal_ansible_type.py b/plugins/filter/reveal_ansible_type.py new file mode 100644 index 0000000000..916aaff930 --- /dev/null +++ b/plugins/filter/reveal_ansible_type.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024 Vladimir Botka +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' + name: reveal_ansible_type + short_description: Return input type + version_added: "9.2.0" + author: Vladimir Botka (@vbotka) + description: This filter returns input type. + options: + _input: + description: Input data. + type: raw + required: true + alias: + description: Data type aliases. + default: {} + type: dictionary +''' + +EXAMPLES = ''' +# Substitution converts str to AnsibleUnicode +# ------------------------------------------- + +# String. AnsibleUnicode. +data: "abc" +result: '{{ data | community.general.reveal_ansible_type }}' +# result => AnsibleUnicode + +# String. AnsibleUnicode alias str. +alias: {"AnsibleUnicode": "str"} +data: "abc" +result: '{{ data | community.general.reveal_ansible_type(alias) }}' +# result => str + +# List. All items are AnsibleUnicode. +data: ["a", "b", "c"] +result: '{{ data | community.general.reveal_ansible_type }}' +# result => list[AnsibleUnicode] + +# Dictionary. All keys are AnsibleUnicode. All values are AnsibleUnicode. +data: {"a": "foo", "b": "bar", "c": "baz"} +result: '{{ data | community.general.reveal_ansible_type }}' +# result => dict[AnsibleUnicode, AnsibleUnicode] + +# No substitution and no alias. Type of strings is str +# ---------------------------------------------------- + +# String +result: '{{ "abc" | community.general.reveal_ansible_type }}' +# result => str + +# Integer +result: '{{ 123 | community.general.reveal_ansible_type }}' +# result => int + +# Float +result: '{{ 123.45 | community.general.reveal_ansible_type }}' +# result => float + +# Boolean +result: '{{ true | community.general.reveal_ansible_type }}' +# result => bool + +# List. All items are strings. +result: '{{ ["a", "b", "c"] | community.general.reveal_ansible_type }}' +# result => list[str] + +# List of dictionaries. +result: '{{ [{"a": 1}, {"b": 2}] | community.general.reveal_ansible_type }}' +# result => list[dict] + +# Dictionary. All keys are strings. All values are integers. +result: '{{ {"a": 1} | community.general.reveal_ansible_type }}' +# result => dict[str, int] + +# Dictionary. All keys are strings. All values are integers. +result: '{{ {"a": 1, "b": 2} | community.general.reveal_ansible_type }}' +# result => dict[str, int] + +# Type of strings is AnsibleUnicode or str +# ---------------------------------------- + +# Dictionary. The keys are integers or strings. All values are strings. +alias: {"AnsibleUnicode": "str"} +data: {1: 'a', 'b': 'b'} +result: '{{ data | community.general.reveal_ansible_type(alias) }}' +# result => dict[int|str, str] + +# Dictionary. All keys are integers. All values are keys. +alias: {"AnsibleUnicode": "str"} +data: {1: 'a', 2: 'b'} +result: '{{ data | community.general.reveal_ansible_type(alias) }}' +# result => dict[int, str] + +# Dictionary. All keys are strings. Multiple types values. +alias: {"AnsibleUnicode": "str"} +data: {'a': 1, 'b': 1.1, 'c': 'abc', 'd': True, 'e': ['x', 'y', 'z'], 'f': {'x': 1, 'y': 2}} +result: '{{ data | community.general.reveal_ansible_type(alias) }}' +# result => dict[str, bool|dict|float|int|list|str] + +# List. Multiple types items. +alias: {"AnsibleUnicode": "str"} +data: [1, 2, 1.1, 'abc', True, ['x', 'y', 'z'], {'x': 1, 'y': 2}] +result: '{{ data | community.general.reveal_ansible_type(alias) }}' +# result => list[bool|dict|float|int|list|str] +''' + +RETURN = ''' + _value: + description: Type of the data. + type: str +''' + +from ansible_collections.community.general.plugins.plugin_utils.ansible_type import _ansible_type + + +def reveal_ansible_type(data, alias=None): + """Returns data type""" + + return _ansible_type(data, alias) + + +class FilterModule(object): + + def filters(self): + return { + 'reveal_ansible_type': reveal_ansible_type + } diff --git a/plugins/plugin_utils/ansible_type.py b/plugins/plugin_utils/ansible_type.py new file mode 100644 index 0000000000..ab78b78927 --- /dev/null +++ b/plugins/plugin_utils/ansible_type.py @@ -0,0 +1,47 @@ +# Copyright (c) 2024 Vladimir Botka +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.errors import AnsibleFilterError +from ansible.module_utils.common._collections_compat import Mapping + + +def _atype(data, alias): + """ + Returns the name of the type class. + """ + + data_type = type(data).__name__ + return alias.get(data_type, data_type) + + +def _ansible_type(data, alias): + """ + Returns the Ansible data type. + """ + + if alias is None: + alias = {} + + if not isinstance(alias, Mapping): + msg = "The argument alias must be a dictionary. %s is %s" + raise AnsibleFilterError(msg % (alias, type(alias))) + + data_type = _atype(data, alias) + + if data_type == 'list' and len(data) > 0: + items = [_atype(i, alias) for i in data] + items_type = '|'.join(sorted(set(items))) + return ''.join((data_type, '[', items_type, ']')) + + if data_type == 'dict' and len(data) > 0: + keys = [_atype(i, alias) for i in data.keys()] + vals = [_atype(i, alias) for i in data.values()] + keys_type = '|'.join(sorted(set(keys))) + vals_type = '|'.join(sorted(set(vals))) + return ''.join((data_type, '[', keys_type, ', ', vals_type, ']')) + + return data_type diff --git a/plugins/test/ansible_type.py b/plugins/test/ansible_type.py new file mode 100644 index 0000000000..9ac5e138eb --- /dev/null +++ b/plugins/test/ansible_type.py @@ -0,0 +1,203 @@ +# Copyright (c) 2024 Vladimir Botka +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' + name: ansible_type + short_description: Validate input type + version_added: "9.2.0" + author: Vladimir Botka (@vbotka) + description: This test validates input type. + options: + _input: + description: Input data. + type: raw + required: true + dtype: + description: A single data type, or a data types list to be validated. + type: raw + required: true + alias: + description: Data type aliases. + default: {} + type: dictionary +''' + +EXAMPLES = ''' + +# Substitution converts str to AnsibleUnicode +# ------------------------------------------- + +# String. AnsibleUnicode. +dtype: AnsibleUnicode +data: "abc" +result: '{{ data is community.general.ansible_type(dtype) }}' +# result => true + +# String. AnsibleUnicode alias str. +alias: {"AnsibleUnicode": "str"} +dtype: str +data: "abc" +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true + +# List. All items are AnsibleUnicode. +dtype: list[AnsibleUnicode] +data: ["a", "b", "c"] +result: '{{ data is community.general.ansible_type(dtype) }}' +# result => true + +# Dictionary. All keys are AnsibleUnicode. All values are AnsibleUnicode. +dtype: dict[AnsibleUnicode, AnsibleUnicode] +data: {"a": "foo", "b": "bar", "c": "baz"} +result: '{{ data is community.general.ansible_type(dtype) }}' +# result => true + +# No substitution and no alias. Type of strings is str +# ---------------------------------------------------- + +# String +dtype: str +result: '{{ "abc" is community.general.ansible_type(dtype) }}' +# result => true + +# Integer +dtype: int +result: '{{ 123 is community.general.ansible_type(dtype) }}' +# result => true + +# Float +dtype: float +result: '{{ 123.45 is community.general.ansible_type(dtype) }}' +# result => true + +# Boolean +dtype: bool +result: '{{ true is community.general.ansible_type(dtype) }}' +# result => true + +# List. All items are strings. +dtype: list[str] +result: '{{ ["a", "b", "c"] is community.general.ansible_type(dtype) }}' +# result => true + +# List of dictionaries. +dtype: list[dict] +result: '{{ [{"a": 1}, {"b": 2}] is community.general.ansible_type(dtype) }}' +# result => true + +# Dictionary. All keys are strings. All values are integers. +dtype: dict[str, int] +result: '{{ {"a": 1} is community.general.ansible_type(dtype) }}' +# result => true + +# Dictionary. All keys are strings. All values are integers. +dtype: dict[str, int] +result: '{{ {"a": 1, "b": 2} is community.general.ansible_type(dtype) }}' +# result => true + +# Type of strings is AnsibleUnicode or str +# ---------------------------------------- + +# Dictionary. The keys are integers or strings. All values are strings. +alias: {"AnsibleUnicode": "str"} +dtype: dict[int|str, str] +data: {1: 'a', 'b': 'b'} +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true + +# Dictionary. All keys are integers. All values are keys. +alias: {"AnsibleUnicode": "str"} +dtype: dict[int, str] +data: {1: 'a', 2: 'b'} +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true + +# Dictionary. All keys are strings. Multiple types values. +alias: {"AnsibleUnicode": "str"} +dtype: dict[str, bool|dict|float|int|list|str] +data: {'a': 1, 'b': 1.1, 'c': 'abc', 'd': True, 'e': ['x', 'y', 'z'], 'f': {'x': 1, 'y': 2}} +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true + +# List. Multiple types items. +alias: {"AnsibleUnicode": "str"} +dtype: list[bool|dict|float|int|list|str] +data: [1, 2, 1.1, 'abc', True, ['x', 'y', 'z'], {'x': 1, 'y': 2}] +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true + +# Option dtype is list +# -------------------- + +# AnsibleUnicode or str +dtype: ['AnsibleUnicode', 'str'] +data: abc +result: '{{ data is community.general.ansible_type(dtype) }}' +# result => true + +# float or int +dtype: ['float', 'int'] +data: 123 +result: '{{ data is community.general.ansible_type(dtype) }}' +# result => true + +# float or int +dtype: ['float', 'int'] +data: 123.45 +result: '{{ data is community.general.ansible_type(dtype) }}' +# result => true + +# Multiple alias +# -------------- + +# int alias number +alias: {"int": "number", "float": "number"} +dtype: number +data: 123 +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true + +# float alias number +alias: {"int": "number", "float": "number"} +dtype: number +data: 123.45 +result: '{{ data is community.general.ansible_type(dtype, alias) }}' +# result => true +''' + +RETURN = ''' + _value: + description: Whether the data type is valid. + type: bool +''' + +from ansible.errors import AnsibleFilterError +from ansible.module_utils.common._collections_compat import Sequence +from ansible_collections.community.general.plugins.plugin_utils.ansible_type import _ansible_type + + +def ansible_type(data, dtype, alias=None): + """Validates data type""" + + if not isinstance(dtype, Sequence): + msg = "The argument dtype must be a string or a list. dtype is %s." + raise AnsibleFilterError(msg % (dtype, type(dtype))) + + if isinstance(dtype, str): + data_types = [dtype] + else: + data_types = dtype + + return _ansible_type(data, alias) in data_types + + +class TestModule(object): + + def tests(self): + return { + 'ansible_type': ansible_type + } diff --git a/tests/integration/targets/filter_reveal_ansible_type/aliases b/tests/integration/targets/filter_reveal_ansible_type/aliases new file mode 100644 index 0000000000..12d1d6617e --- /dev/null +++ b/tests/integration/targets/filter_reveal_ansible_type/aliases @@ -0,0 +1,5 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/posix/2 diff --git a/tests/integration/targets/filter_reveal_ansible_type/tasks/main.yml b/tests/integration/targets/filter_reveal_ansible_type/tasks/main.yml new file mode 100644 index 0000000000..c890c11901 --- /dev/null +++ b/tests/integration/targets/filter_reveal_ansible_type/tasks/main.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Integration tests + import_tasks: tasks.yml diff --git a/tests/integration/targets/filter_reveal_ansible_type/tasks/tasks.yml b/tests/integration/targets/filter_reveal_ansible_type/tasks/tasks.yml new file mode 100644 index 0000000000..37d3abcb71 --- /dev/null +++ b/tests/integration/targets/filter_reveal_ansible_type/tasks/tasks.yml @@ -0,0 +1,185 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# Substitution converts str to AnsibleUnicode +# ------------------------------------------- + +- name: String. AnsibleUnicode. + assert: + that: result == dtype + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: "abc" + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: 'AnsibleUnicode' + +- name: String. AnsibleUnicode alias str. + assert: + that: result == dtype + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: "abc" + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: 'str' + +- name: List. All items are AnsibleUnicode. + assert: + that: result == dtype + success_msg: '["a", "b", "c"] is {{ dtype }}' + fail_msg: '["a", "b", "c"] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: ["a", "b", "c"] + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: 'list[AnsibleUnicode]' + +- name: Dictionary. All keys are AnsibleUnicode. All values are AnsibleUnicode. + assert: + that: result == dtype + success_msg: '{"a": "foo", "b": "bar", "c": "baz"} is {{ dtype }}' + fail_msg: '{"a": "foo", "b": "bar", "c": "baz"} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: {"a": "foo", "b": "bar", "c": "baz"} + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: 'dict[AnsibleUnicode, AnsibleUnicode]' + +# No substitution and no alias. Type of strings is str +# ---------------------------------------------------- + +- name: String + assert: + that: result == dtype + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ "abc" | community.general.reveal_ansible_type }}' + dtype: str + +- name: Integer + assert: + that: result == dtype + success_msg: '123 is {{ dtype }}' + fail_msg: '123 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ 123 | community.general.reveal_ansible_type }}' + dtype: int + +- name: Float + assert: + that: result == dtype + success_msg: '123.45 is {{ dtype }}' + fail_msg: '123.45 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ 123.45 | community.general.reveal_ansible_type }}' + dtype: float + +- name: Boolean + assert: + that: result == dtype + success_msg: 'true is {{ dtype }}' + fail_msg: 'true is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ true | community.general.reveal_ansible_type }}' + dtype: bool + +- name: List. All items are strings. + assert: + that: result == dtype + success_msg: '["a", "b", "c"] is {{ dtype }}' + fail_msg: '["a", "b", "c"] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ ["a", "b", "c"] | community.general.reveal_ansible_type }}' + dtype: list[str] + +- name: List of dictionaries. + assert: + that: result == dtype + success_msg: '[{"a": 1}, {"b": 2}] is {{ dtype }}' + fail_msg: '[{"a": 1}, {"b": 2}] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ [{"a": 1}, {"b": 2}] | community.general.reveal_ansible_type }}' + dtype: list[dict] + +- name: Dictionary. All keys are strings. All values are integers. + assert: + that: result == dtype + success_msg: '{"a": 1} is {{ dtype }}' + fail_msg: '{"a": 1} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ {"a": 1} | community.general.reveal_ansible_type }}' + dtype: dict[str, int] + +- name: Dictionary. All keys are strings. All values are integers. + assert: + that: result == dtype + success_msg: '{"a": 1, "b": 2} is {{ dtype }}' + fail_msg: '{"a": 1, "b": 2} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ {"a": 1, "b": 2} | community.general.reveal_ansible_type }}' + dtype: dict[str, int] + +# Type of strings is AnsibleUnicode or str +# ---------------------------------------- + +- name: Dictionary. The keys are integers or strings. All values are strings. + assert: + that: result == dtype + success_msg: 'data is {{ dtype }}' + fail_msg: 'data is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: {1: 'a', 'b': 'b'} + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: dict[int|str, str] + +- name: Dictionary. All keys are integers. All values are keys. + assert: + that: result == dtype + success_msg: 'data is {{ dtype }}' + fail_msg: 'data is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: {1: 'a', 2: 'b'} + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: dict[int, str] + +- name: Dictionary. All keys are strings. Multiple types values. + assert: + that: result == dtype + success_msg: 'data is {{ dtype }}' + fail_msg: 'data is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: {'a': 1, 'b': 1.1, 'c': 'abc', 'd': True, 'e': ['x', 'y', 'z'], 'f': {'x': 1, 'y': 2}} + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: dict[str, bool|dict|float|int|list|str] + +- name: List. Multiple types items. + assert: + that: result == dtype + success_msg: 'data is {{ dtype }}' + fail_msg: 'data is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: [1, 2, 1.1, 'abc', True, ['x', 'y', 'z'], {'x': 1, 'y': 2}] + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: list[bool|dict|float|int|list|str] diff --git a/tests/integration/targets/test_ansible_type/aliases b/tests/integration/targets/test_ansible_type/aliases new file mode 100644 index 0000000000..12d1d6617e --- /dev/null +++ b/tests/integration/targets/test_ansible_type/aliases @@ -0,0 +1,5 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/posix/2 diff --git a/tests/integration/targets/test_ansible_type/tasks/main.yml b/tests/integration/targets/test_ansible_type/tasks/main.yml new file mode 100644 index 0000000000..c890c11901 --- /dev/null +++ b/tests/integration/targets/test_ansible_type/tasks/main.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Integration tests + import_tasks: tasks.yml diff --git a/tests/integration/targets/test_ansible_type/tasks/tasks.yml b/tests/integration/targets/test_ansible_type/tasks/tasks.yml new file mode 100644 index 0000000000..261256c0d4 --- /dev/null +++ b/tests/integration/targets/test_ansible_type/tasks/tasks.yml @@ -0,0 +1,248 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# Substitution converts str to AnsibleUnicode +# ------------------------------------------- + +- name: String. AnsibleUnicode. + assert: + that: data is community.general.ansible_type(dtype) + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: "abc" + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: 'AnsibleUnicode' + +- name: String. AnsibleUnicode alias str. + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: "abc" + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: 'str' + +- name: List. All items are AnsibleUnicode. + assert: + that: data is community.general.ansible_type(dtype) + success_msg: '["a", "b", "c"] is {{ dtype }}' + fail_msg: '["a", "b", "c"] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: ["a", "b", "c"] + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: 'list[AnsibleUnicode]' + +- name: Dictionary. All keys are AnsibleUnicode. All values are AnsibleUnicode. + assert: + that: data is community.general.ansible_type(dtype) + success_msg: '{"a": "foo", "b": "bar", "c": "baz"} is {{ dtype }}' + fail_msg: '{"a": "foo", "b": "bar", "c": "baz"} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: {"a": "foo", "b": "bar", "c": "baz"} + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: 'dict[AnsibleUnicode, AnsibleUnicode]' + +# No substitution and no alias. Type of strings is str +# ---------------------------------------------------- + +- name: String + assert: + that: '"abc" is community.general.ansible_type(dtype)' + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ "abc" | community.general.reveal_ansible_type }}' + dtype: str + +- name: Integer + assert: + that: '123 is community.general.ansible_type(dtype)' + success_msg: '123 is {{ dtype }}' + fail_msg: '123 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ 123 | community.general.reveal_ansible_type }}' + dtype: int + +- name: Float + assert: + that: '123.45 is community.general.ansible_type(dtype)' + success_msg: '123.45 is {{ dtype }}' + fail_msg: '123.45 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ 123.45 | community.general.reveal_ansible_type }}' + dtype: float + +- name: Boolean + assert: + that: 'true is community.general.ansible_type(dtype)' + success_msg: 'true is {{ dtype }}' + fail_msg: 'true is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ true | community.general.reveal_ansible_type }}' + dtype: bool + +- name: List. All items are strings. + assert: + that: '["a", "b", "c"] is community.general.ansible_type(dtype)' + success_msg: '["a", "b", "c"] is {{ dtype }}' + fail_msg: '["a", "b", "c"] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ ["a", "b", "c"] | community.general.reveal_ansible_type }}' + dtype: list[str] + +- name: List of dictionaries. + assert: + that: '[{"a": 1}, {"b": 2}] is community.general.ansible_type(dtype)' + success_msg: '[{"a": 1}, {"b": 2}] is {{ dtype }}' + fail_msg: '[{"a": 1}, {"b": 2}] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ [{"a": 1}, {"b": 2}] | community.general.reveal_ansible_type }}' + dtype: list[dict] + +- name: Dictionary. All keys are strings. All values are integers. + assert: + that: '{"a": 1} is community.general.ansible_type(dtype)' + success_msg: '{"a": 1} is {{ dtype }}' + fail_msg: '{"a": 1} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ {"a": 1} | community.general.reveal_ansible_type }}' + dtype: dict[str, int] + +- name: Dictionary. All keys are strings. All values are integers. + assert: + that: '{"a": 1, "b": 2} is community.general.ansible_type(dtype)' + success_msg: '{"a": 1, "b": 2} is {{ dtype }}' + fail_msg: '{"a": 1, "b": 2} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + result: '{{ {"a": 1, "b": 2} | community.general.reveal_ansible_type }}' + dtype: dict[str, int] + +# Type of strings is AnsibleUnicode or str +# ---------------------------------------- + +- name: Dictionary. The keys are integers or strings. All values are strings. + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '{"1": "a", "b": "b"} is {{ dtype }}' + fail_msg: '{"1": "a", "b": "b"} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: {1: 'a', 'b': 'b'} + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: dict[int|str, str] + +- name: Dictionary. All keys are integers. All values are keys. + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '{"1": "a", "2": "b"} is {{ dtype }}' + fail_msg: '{"1": "a", "2": "b"} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: {1: 'a', 2: 'b'} + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: dict[int, str] + +- name: Dictionary. All keys are strings. Multiple types values. + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '{"a": 1, "b": 1.1, "c": "abc", "d": true, "e": ["x", "y", "z"], "f": {"x": 1, "y": 2}} is {{ dtype }}' + fail_msg: '{"a": 1, "b": 1.1, "c": "abc", "d": true, "e": ["x", "y", "z"], "f": {"x": 1, "y": 2}} is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: {'a': 1, 'b': 1.1, 'c': 'abc', 'd': True, 'e': ['x', 'y', 'z'], 'f': {'x': 1, 'y': 2}} + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: dict[str, bool|dict|float|int|list|str] + +- name: List. Multiple types items. + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '[1, 2, 1.1, "abc", true, ["x", "y", "z"], {"x": 1, "y": 2}] is {{ dtype }}' + fail_msg: '[1, 2, 1.1, "abc", true, ["x", "y", "z"], {"x": 1, "y": 2}] is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"AnsibleUnicode": "str"} + data: [1, 2, 1.1, 'abc', True, ['x', 'y', 'z'], {'x': 1, 'y': 2}] + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: list[bool|dict|float|int|list|str] + +# Option dtype is list +# -------------------- + +- name: AnsibleUnicode or str + assert: + that: data is community.general.ansible_type(dtype) + success_msg: '"abc" is {{ dtype }}' + fail_msg: '"abc" is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: abc + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: ['AnsibleUnicode', 'str'] + +- name: float or int + assert: + that: data is community.general.ansible_type(dtype) + success_msg: '123 is {{ dtype }}' + fail_msg: '123 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: 123 + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: ['float', 'int'] + +- name: float or int + assert: + that: data is community.general.ansible_type(dtype) + success_msg: '123.45 is {{ dtype }}' + fail_msg: '123.45 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + data: 123.45 + result: '{{ data | community.general.reveal_ansible_type }}' + dtype: ['float', 'int'] + +# Multiple alias +# -------------- + +- name: int alias number + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '123 is {{ dtype }}' + fail_msg: '123 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"int": "number", "float": "number"} + data: 123 + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: number + +- name: float alias number + assert: + that: data is community.general.ansible_type(dtype, alias) + success_msg: '123.45 is {{ dtype }}' + fail_msg: '123.45 is {{ result }}' + quiet: '{{ quiet_test | d(true) | bool }}' + vars: + alias: {"int": "number", "float": "number"} + data: 123.45 + result: '{{ data | community.general.reveal_ansible_type(alias) }}' + dtype: number