diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
commit | 7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch) | |
tree | efb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/community/dns/tests | |
parent | Releasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff) | |
download | ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip |
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/dns/tests')
41 files changed, 3371 insertions, 54 deletions
diff --git a/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml b/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml index 43941d042..7c7de6b70 100644 --- a/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml +++ b/ansible_collections/community/dns/tests/ee/roles/wait_for_txt/tasks/main.yml @@ -3,16 +3,12 @@ # 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: Get TXT records for github.com - shell: | - dig -t TXT github.com +short | sed -e 's/" "//g' -e 's/"//g' - register: dig - - name: Wait for existing TXT entry community.dns.wait_for_txt: records: - name: github.com - values: "{{ dig.stdout_lines }}" + values: | + {{ query('community.dns.lookup_as_dict', 'github.com', type='TXT') | map(attribute='value') | list }} - name: github.io values: [] query_timeout: 20 diff --git a/ansible_collections/community/dns/tests/integration/replace-requirements.sh b/ansible_collections/community/dns/tests/integration/replace-requirements.sh new file mode 100755 index 000000000..c3a52f5d1 --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/replace-requirements.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# 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 + +DIRECTORY="$(dirname "$0")" + +if [ -e "${DIRECTORY}/$1" ]; then + cp "${DIRECTORY}/$1" "${DIRECTORY}/requirements.txt" +fi diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.yml b/ansible_collections/community/dns/tests/integration/requirements-stable-2.10.txt index bcecb88e9..af90be668 100644 --- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.yml +++ b/ansible_collections/community/dns/tests/integration/requirements-stable-2.10.txt @@ -1,8 +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 -- hosts: localhost - roles: - - { role: wait_for_txt } +ipaddress ; python_version < '3.3' + +dnspython < 2.4.0 diff --git a/ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt b/ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt new file mode 100644 index 000000000..af90be668 --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/requirements-stable-2.9.txt @@ -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 + +ipaddress ; python_version < '3.3' + +dnspython < 2.4.0 diff --git a/ansible_collections/community/dns/tests/integration/requirements.txt b/ansible_collections/community/dns/tests/integration/requirements.txt new file mode 100644 index 000000000..5e4bca63f --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/requirements.txt @@ -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 + +ipaddress ; python_version < '3.3' + +dnspython diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/aliases b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/aliases new file mode 100644 index 000000000..abc0d5476 --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/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 + +shippable/posix/group1 diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml new file mode 100644 index 000000000..52ba9a46c --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup/tasks/main.yml @@ -0,0 +1,26 @@ +--- +# 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: Look up some records + ansible.builtin.set_fact: + ansible_a: >- + {{ query('community.dns.lookup', 'ansible.com', type='A') }} + ansible_aaaa: >- + {{ query('community.dns.lookup', 'ansible.com', type='AAAA', server='9.9.9.9') }} + ansible_txt: >- + {{ query('community.dns.lookup', 'ansible.com', type='TXT', server=['1.1.1.1', '8.8.8.8', 'dns9.quad9.net.']) }} + ansible_empty: >- + {{ query('community.dns.lookup', 'does-not-exist.ansible.com') }} + +- name: Check results + assert: + that: + - ansible_a | length > 0 + - ansible_a[0] is regex("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") + - ansible_aaaa | length > 0 + - ansible_aaaa[0] is regex("^[0-9a-fA-F:]+$") + - ansible_txt | length > 0 + - ansible_txt[0] is string + - ansible_empty | length == 0 diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/aliases b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/aliases new file mode 100644 index 000000000..abc0d5476 --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/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 + +shippable/posix/group1 diff --git a/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml new file mode 100644 index 000000000..3ba6f52f0 --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/targets/lookup_lookup_as_dict/tasks/main.yml @@ -0,0 +1,31 @@ +--- +# 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: Look up some records + ansible.builtin.set_fact: + ansible_a: >- + {{ query('community.dns.lookup_as_dict', 'ansible.com', type='A') }} + ansible_aaaa: >- + {{ query('community.dns.lookup_as_dict', 'ansible.com', type='AAAA', server='9.9.9.9') }} + ansible_txt: >- + {{ query('community.dns.lookup_as_dict', 'ansible.com', type='TXT', server=['1.1.1.1', '8.8.8.8', 'dns9.quad9.net.']) }} + ansible_empty: >- + {{ query('community.dns.lookup_as_dict', 'does-not-exist.ansible.com') }} + +- name: Check results + assert: + that: + - ansible_a | length > 0 + - ansible_a[0].address is string + - ansible_a[0].address is regex("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") + - ansible_aaaa | length > 0 + - ansible_aaaa[0].address is string + - ansible_aaaa[0].address is regex("^[0-9a-fA-F:]+$") + - ansible_txt | length > 0 + - ansible_txt[0].strings is sequence + - ansible_txt[0].strings[0] is string + - ansible_txt[0].value is string + - ansible_txt | map(attribute='strings') | map('join') | list == ansible_txt | map(attribute='value') | list + - ansible_empty | length == 0 diff --git a/ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml new file mode 100644 index 000000000..0b074df5a --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/targets/nameserver_info/tasks/main.yml @@ -0,0 +1,78 @@ +--- +# 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: Retrieve name servers of two DNS names + community.dns.nameserver_info: + name: + - www.example.com + - example.org + register: result + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result }}' + +- name: Show nameservers for www.example.com + ansible.builtin.debug: + msg: '{{ result.results[0].nameservers }}' + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result.results[0].nameservers[0] }}' + +- name: Validate results + assert: + that: + - result.results[0].nameservers[0] == 'a.iana-servers.net.' + - result.results[0].nameservers[1] == 'b.iana-servers.net.' + - result.results[0].nameservers[0] != 'b.iana-servers.net.' + +- name: Retrieve name servers of two DNS names using custom DNS servers + community.dns.nameserver_info: + name: + - www.example.com + - example.org + server: + # Quad9 servers (https://en.wikipedia.org/wiki/Quad9#Service) + - 9.9.9.9 + - 149.112.112.112 + - 2620:fe::9 + - 2620:fe::fe + register: result + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result }}' + +- name: Show nameservers for www.example.com + ansible.builtin.debug: + msg: '{{ result.results[0].nameservers }}' + +- name: Show TXT values for www.example.com for first nameserver + ansible.builtin.debug: + msg: '{{ result.results[0].nameservers[0] }}' + +- name: Validate results + assert: + that: + - result.results[0].nameservers[0] == 'a.iana-servers.net.' + - result.results[0].nameservers[1] == 'b.iana-servers.net.' + - result.results[0].nameservers[0] != 'b.iana-servers.net.' + +- name: Retrieve name servers of a DNS name that do not exist + community.dns.nameserver_info: + name: + - foo.bar.example.com + register: result + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result }}' + +- name: Validate results + assert: + that: + - result.results[0].nameservers[0] == 'a.iana-servers.net.' + - result.results[0].nameservers[1] == 'b.iana-servers.net.' diff --git a/ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml new file mode 100644 index 000000000..b03b641fe --- /dev/null +++ b/ansible_collections/community/dns/tests/integration/targets/nameserver_record_info/tasks/main.yml @@ -0,0 +1,56 @@ +--- +# 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: Retrieve TXT values from all nameservers for two DNS names + community.dns.nameserver_record_info: + name: + - www.example.com + - example.org + type: TXT + register: result + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result }}' + +- name: Show TXT values for www.example.com for first nameserver + ansible.builtin.debug: + msg: '{{ result.results[0].result[0].nameserver }}' + +- name: Validate results + assert: + that: + - result.results[0].result[0].nameserver == 'a.iana-servers.net.' + - result.results[0].result[1].nameserver == 'b.iana-servers.net.' + - result.results[0].result[0].nameserver != 'b.iana-servers.net.' + +- name: Retrieve TXT values from all nameservers for two DNS names using custom DNS servers + community.dns.nameserver_record_info: + name: + - www.example.com + - example.org + type: TXT + server: + # Quad9 servers (https://en.wikipedia.org/wiki/Quad9#Service) + - 9.9.9.9 + - 149.112.112.112 + - 2620:fe::9 + - 2620:fe::fe + register: result + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result }}' + +- name: Show TXT values for www.example.com for all nameservers + ansible.builtin.debug: + msg: '{{ result.results[0].result[0].nameserver }}' + +- name: Validate results + assert: + that: + - result.results[0].result[0].nameserver == 'a.iana-servers.net.' + - result.results[0].result[1].nameserver == 'b.iana-servers.net.' + - result.results[0].result[0].nameserver != 'b.iana-servers.net.' diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases index 34334fd82..abc0d5476 100644 --- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases +++ b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/aliases @@ -3,4 +3,3 @@ # SPDX-License-Identifier: GPL-3.0-or-later shippable/posix/group1 -destructive diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh deleted file mode 100755 index 9265e7ee1..000000000 --- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/runme.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# 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 - -set -eux - -export ANSIBLE_TEST_PREFER_VENV=1 # see https://github.com/ansible/ansible/pull/73000#issuecomment-757012395; can be removed once Ansible 2.9 and ansible-base 2.10 support has been dropped -source virtualenv.sh - -# Requirements have to be installed prior to running ansible-playbook -# because plugins and requirements are loaded before the task runs - -pip install dnspython - -ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" diff --git a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml index fce443423..b706cfcae 100644 --- a/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml +++ b/ansible_collections/community/dns/tests/integration/targets/wait_for_txt/tasks/main.yml @@ -8,7 +8,7 @@ records: - name: github.com values: | - {{ query('community.general.dig', 'github.com', 'qtype=TXT') | map('regex_replace', '" "', '') | list }} + {{ query('community.dns.lookup_as_dict', 'github.com', type='TXT') | map(attribute='value') | list }} - name: github.io values: [] query_timeout: 20 diff --git a/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py b/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py index c636beb08..251e6d70f 100755 --- a/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py +++ b/ansible_collections/community/dns/tests/sanity/extra/extra-docs.py @@ -17,7 +17,7 @@ def main(): suffix = ':{env}'.format(env=env["ANSIBLE_COLLECTIONS_PATH"]) if 'ANSIBLE_COLLECTIONS_PATH' in env else '' env['ANSIBLE_COLLECTIONS_PATH'] = '{root}{suffix}'.format(root=os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd()))), suffix=suffix) p = subprocess.run( - ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--disallow-semantic-markup', '--skip-rstcheck', '.'], + ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--skip-rstcheck', '.'], env=env, check=False, ) diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt index dc9da1161..14ec3bedc 100644 --- a/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.10.txt @@ -1 +1,8 @@ +docs/docsite/rst/filter_guide.rst rstcheck +docs/docsite/rst/hosttech_guide.rst rstcheck +docs/docsite/rst/hetzner_guide.rst rstcheck +plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt index dc9da1161..593a5082f 100644 --- a/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.11.txt @@ -1 +1,5 @@ +plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt index dc9da1161..593a5082f 100644 --- a/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.12.txt @@ -1 +1,5 @@ +plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt index dc9da1161..593a5082f 100644 --- a/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.13.txt @@ -1 +1,5 @@ +plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt index dc9da1161..593a5082f 100644 --- a/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.14.txt @@ -1 +1,5 @@ +plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt new file mode 100644 index 000000000..dc9da1161 --- /dev/null +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt @@ -0,0 +1 @@ +plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.17.txt.license @@ -0,0 +1,3 @@ +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 +SPDX-FileCopyrightText: Ansible Project diff --git a/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt b/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt index dc9da1161..14ec3bedc 100644 --- a/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt +++ b/ansible_collections/community/dns/tests/sanity/ignore-2.9.txt @@ -1 +1,8 @@ +docs/docsite/rst/filter_guide.rst rstcheck +docs/docsite/rst/hosttech_guide.rst rstcheck +docs/docsite/rst/hetzner_guide.rst rstcheck +plugins/modules/hetzner_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hetzner_dns_record_set_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_info.py validate-modules:invalid-documentation +plugins/modules/hosttech_dns_record_set_info.py validate-modules:invalid-documentation plugins/public_suffix_list.dat no-smart-quotes diff --git a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py index 3b01de586..bfaa4886d 100644 --- a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py +++ b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hetzner_dns_records.py @@ -12,6 +12,7 @@ import textwrap from ansible import constants as C from ansible.inventory.manager import InventoryManager from ansible.module_utils.common.text.converters import to_native +from ansible.utils.unsafe_proxy import AnsibleUnsafe from ansible_collections.community.internal_test_tools.tests.unit.mock.path import mock_unfrackpath_noop from ansible_collections.community.internal_test_tools.tests.unit.mock.loader import DictDataLoader @@ -208,6 +209,8 @@ def test_inventory_file_simple(mocker): assert im._inventory.get_host('*.example.com') in im._inventory.groups['ungrouped'].hosts assert im._inventory.get_host('example.com').get_vars()['ansible_host'] == '1.2.3.4' assert im._inventory.get_host('*.example.com').get_vars()['ansible_host'] == '1.2.3.5' + assert isinstance(im._inventory.get_host('example.com').get_vars()['ansible_host'], AnsibleUnsafe) + assert isinstance(im._inventory.get_host('*.example.com').get_vars()['ansible_host'], AnsibleUnsafe) assert len(im._inventory.groups['ungrouped'].hosts) == 2 assert len(im._inventory.groups['all'].hosts) == 0 diff --git a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py index a7adb2c09..cefd02a06 100644 --- a/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py +++ b/ansible_collections/community/dns/tests/unit/plugins/inventory/test_hosttech_dns_records.py @@ -12,6 +12,7 @@ import textwrap from ansible import constants as C from ansible.inventory.manager import InventoryManager from ansible.module_utils.common.text.converters import to_native +from ansible.utils.unsafe_proxy import AnsibleUnsafe from ansible_collections.community.internal_test_tools.tests.unit.mock.path import mock_unfrackpath_noop from ansible_collections.community.internal_test_tools.tests.unit.mock.loader import DictDataLoader @@ -185,6 +186,8 @@ def test_inventory_file_simple(mocker): assert im._inventory.get_host('*.example.com') in im._inventory.groups['ungrouped'].hosts assert im._inventory.get_host('example.com').get_vars()['ansible_host'] == '1.2.3.4' assert im._inventory.get_host('*.example.com').get_vars()['ansible_host'] == '1.2.3.5' + assert isinstance(im._inventory.get_host('example.com').get_vars()['ansible_host'], AnsibleUnsafe) + assert isinstance(im._inventory.get_host('*.example.com').get_vars()['ansible_host'], AnsibleUnsafe) assert len(im._inventory.groups['ungrouped'].hosts) == 2 assert len(im._inventory.groups['all'].hosts) == 0 diff --git a/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py new file mode 100644 index 000000000..0982d8704 --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup.py @@ -0,0 +1,498 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible.errors import AnsibleLookupError +from ansible.plugins.loader import lookup_loader + +from ansible_collections.community.internal_test_tools.tests.unit.compat.unittest import TestCase +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock + +from ..module_utils.resolver_helper import ( + mock_resolver, + mock_query_udp, + create_mock_answer, +) + +# We need dnspython +dns = pytest.importorskip('dns') + + +class TestLookup(TestCase): + def setUp(self): + self.lookup = lookup_loader.get("community.dns.lookup") + + def test_simple(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'www.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com']) + + print(result) + assert len(result) == 2 + assert result[0] == '127.0.0.1' + assert result[1] == '127.0.0.2' + + def test_retry_success(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'www.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com']) + + print(result) + assert len(result) == 2 + assert result[0] == '127.0.0.1' + assert result[1] == '127.0.0.2' + + def test_retry_fail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0].startswith('Unexpected DNS error for www.example.com: The DNS operation timed out after 10') + + def test_simple_nxdomain(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com']) + + print(result) + assert len(result) == 0 + + def test_simple_nxdomain(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com']) + + print(result) + assert len(result) == 0 + + def test_simple_nxdomain_empty(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com'], nxdomain_handling='empty') + + print(result) + assert len(result) == 0 + + def test_simple_nxdomain_message(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com'], nxdomain_handling='message') + + print(result) + assert len(result) == 1 + assert result[0] == 'NXDOMAIN' + + def test_simple_nxdomain_fail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], nxdomain_handling='fail') + + print(exc.value.args[0]) + assert exc.value.args[0] == 'Got NXDOMAIN when querying www.example.com' + + def test_simple_servfail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']" + + def test_retry_servfail_success(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com'], servfail_retries=2) + + print(result) + assert len(result) == 0 + + def test_retry_servfail_fail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], servfail_retries=2) + + print(exc.value.args[0]) + assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']" + + def test_type(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, '"foo bar"'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'baz bam'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'bar'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['example.com'], type='TXT') + + print(result) + assert len(result) == 3 + assert result[0] == '"foo bar"' + assert result[1] == '"baz" "bam"' + assert result[2] == '"bar"' + + def test_server(self): + resolver = mock_resolver(['1.1.1.1'], { + ('2.2.2.2', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', '3.3.3.3']) + + print(result) + assert len(result) == 1 + assert result[0] == '::1' + + def test_server_resolve(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '1.2.3.4'), + )), + }, + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1::2'), + )), + }, + ], + ('1.2.3.4', '1::2', '2.2.2.2', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', 'ns.example.com', '3.3.3.3']) + + print(result) + assert len(result) == 1 + assert result[0] == '::1' + + def test_server_resolve_nxdomain(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0] == 'Unexpected DNS error for ns.example.com: The DNS query name does not exist: ns.example.com.' + + def test_server_resolve_servfail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0] == "Unexpected resolving error for ns.example.com: Error SERVFAIL while querying ['1.1.1.1']" + + def test_server_resolve_empty_lifetime(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 5, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 5, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + ('3.3.3.3', ): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 5, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run( + ['example.org'], + type='AAAA', + server=['ns.example.com', '3.3.3.3'], + query_timeout=5, + ) + + print(result) + assert len(result) == 0 diff --git a/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py new file mode 100644 index 000000000..f29d73143 --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/lookup/test_lookup_as_dict.py @@ -0,0 +1,461 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible.errors import AnsibleLookupError +from ansible.plugins.loader import lookup_loader + +from ansible_collections.community.internal_test_tools.tests.unit.compat.unittest import TestCase +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock + +from ..module_utils.resolver_helper import ( + mock_resolver, + mock_query_udp, + create_mock_answer, +) + +# We need dnspython +dns = pytest.importorskip('dns') + + +class TestLookupAsDict(TestCase): + def setUp(self): + self.lookup = lookup_loader.get("community.dns.lookup_as_dict") + + def test_simple(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'www.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com']) + + print(result) + assert len(result) == 2 + assert result[0] == { + 'address': '127.0.0.1', + } + assert result[1] == { + 'address': '127.0.0.2', + } + + def test_retry_success(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'www.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.2'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com']) + + print(result) + assert len(result) == 2 + assert result[0] == { + 'address': '127.0.0.1', + } + assert result[1] == { + 'address': '127.0.0.2', + } + + def test_retry_fail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'raise': dns.exception.Timeout(timeout=10), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0].startswith('Unexpected DNS error for www.example.com: The DNS operation timed out after 10') + + def test_simple_nxdomain_empty(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com'], nxdomain_handling='empty') + + print(result) + assert len(result) == 0 + + def test_simple_nxdomain_fail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], nxdomain_handling='fail') + + print(exc.value.args[0]) + assert exc.value.args[0] == 'Got NXDOMAIN when querying www.example.com' + + def test_simple_servfail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']" + + def test_retry_servfail_success(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['www.example.com'], servfail_retries=2) + + print(result) + assert len(result) == 0 + + def test_retry_servfail_fail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], servfail_retries=2) + + print(exc.value.args[0]) + assert exc.value.args[0] == "Unexpected resolving error for www.example.com: Error SERVFAIL while querying ['1.1.1.1']" + + def test_type(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, '"foo bar"'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'baz bam'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'bar'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['example.com'], type='TXT') + + print(result) + assert len(result) == 3 + assert result[0] == { + 'strings': ['foo bar'], + 'value': 'foo bar', + } + assert result[1] == { + 'strings': ['baz', 'bam'], + 'value': 'bazbam', + } + assert result[2] == { + 'strings': ['bar'], + 'value': 'bar', + } + + def test_server(self): + resolver = mock_resolver(['1.1.1.1'], { + ('2.2.2.2', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', '3.3.3.3']) + + print(result) + assert len(result) == 1 + assert result[0] == { + 'address': '::1', + } + + def test_server_resolve(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '1.2.3.4'), + )), + }, + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1::2'), + )), + }, + ], + ('1.2.3.4', '1::2', '2.2.2.2', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::1'), + )), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run(['example.org'], type='AAAA', server=['2.2.2.2', 'ns.example.com', '3.3.3.3']) + + print(result) + assert len(result) == 1 + assert result[0] == { + 'address': '::1', + } + + def test_server_resolve_nxdomain(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0] == 'Unexpected DNS error for ns.example.com: The DNS query name does not exist: ns.example.com.' + + def test_server_resolve_servfail(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + with pytest.raises(AnsibleLookupError) as exc: + self.lookup.run(['www.example.com'], server=['2.2.2.2', 'ns.example.com']) + + print(exc.value.args[0]) + assert exc.value.args[0] == "Unexpected resolving error for ns.example.com: Error SERVFAIL while querying ['1.1.1.1']" + + def test_server_resolve_empty_lifetime(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.A, + 'lifetime': 5, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + { + 'target': dns.name.from_unicode(u'ns.example.com'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 5, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + ('3.3.3.3', ): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 5, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + }) + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp([])): + result = self.lookup.run( + ['example.org'], + type='AAAA', + server=['ns.example.com', '3.3.3.3'], + query_timeout=5, + ) + + print(result) + assert len(result) == 0 diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py index e5984444b..664b6db6d 100644 --- a/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py +++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/conversion/test_txt.py @@ -138,6 +138,16 @@ TEST_ENCODE_DECODE = [ True, True, 'decimal' ), ( + # Avoid splitting up an escape into multiple TXT strings + u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB' + u'CDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123' + u'456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdef"\\', + u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB' + u'CDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123' + u'456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdef \\"\\\\', + False, True, 'decimal' + ), + ( # Avoid splitting up an decimal sequence into multiple TXT strings u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzAB' u'CDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123' diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py index e410d2076..7fa213c9f 100644 --- a/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py +++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/resolver_helper.py @@ -11,6 +11,11 @@ __metaclass__ = type from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import MagicMock +try: + import dns.rcode +except ImportError: + pass + def mock_resolver(default_nameservers, nameserver_resolve_sequence): def create_resolver(configure=True): @@ -59,15 +64,16 @@ def mock_query_udp(call_sequence): return udp -def create_mock_answer(rrset=None): - answer = MagicMock() - answer.rrset = rrset - return answer - - def create_mock_response(rcode, authority=None, answer=None): response = MagicMock() response.rcode = MagicMock(return_value=rcode) response.authority = authority or [] response.answer = answer or [] return response + + +def create_mock_answer(rrset=None, rcode=None): + answer = MagicMock() + answer.response = create_mock_response(dns.rcode.NOERROR if rcode is None else rcode, answer=[rrset] if rrset else None) + answer.rrset = rrset + return answer diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py new file mode 100644 index 000000000..9594b8097 --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_dnspython_records.py @@ -0,0 +1,376 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible_collections.community.dns.plugins.module_utils.dnspython_records import ( + RDTYPE_TO_FIELDS, + convert_rdata_to_dict, +) + +# We need dnspython +dns = pytest.importorskip('dns') + +import dns.version + + +TEST_CONVERT_RDATA_TO_DICT = [ + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'address': '3.3.3.3', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'address': '1:2::3', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '::'), + {'to_unicode': False, 'add_synthetic': True}, + { + 'address': '::', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CAA, '10 issue letsencrypt.org'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'flags': 10, + 'tag': 'issue', + 'value': 'letsencrypt.org', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'foo.example.com.'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'target': 'foo.example.com.', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNAME, 'foo.example.com.'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'target': 'foo.example.com.', + }, + ), + ( + dns.rdata.from_text( + dns.rdataclass.IN, + dns.rdatatype.DNSKEY, + '512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3' + ' GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o jqf0BaqHT+8=', + ), + {'to_unicode': False, 'add_synthetic': False}, + { + 'flags': 512, + 'algorithm': 1, + 'protocol': 255, + 'key': ( + 'AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aRyzWZriO6i2odGWWQVucZqKVsENW9' + '1IOW4vqudngPZsY3GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5ojqf0BaqHT+8=' + ), + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, '12345 3 1 123456789abcdef67890123456789abcdef67890'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'algorithm': 3, + 'digest_type': 1, + 'key_tag': 12345, + 'digest': '123456789abcdef67890123456789abcdef67890', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.HINFO, '"Generic PC clone" "NetBSD-1.4"'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'cpu': b'Generic PC clone', + 'os': b'NetBSD-1.4', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.LOC, '60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'latitude': [60, 9, 0, 0, 1], + 'longitude': [24, 39, 0, 0, 1], + 'altitude': 1000.0, + 'size': 2000.0, + 'horizontal_precision': 200000.0, + 'vertical_precision': 2000.0, + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '10 mail.example.com'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'preference': 10, + 'exchange': 'mail.example.com', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NAPTR, '65535 65535 "text 1" "text 2" "text 3" example.com.'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'order': 65535, + 'preference': 65535, + 'flags': b'text 1', + 'service': b'text 2', + 'regexp': b'text 3', + 'replacement': 'example.com.', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.org.'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'target': 'ns.example.org.', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC, 'a.secure A MX RRSIG NSEC TYPE1234'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'next': 'a.secure', + 'windows': 'A MX RRSIG NSEC TYPE1234', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, '1 1 123 f00baa23 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'algorithm': 1, + 'flags': 1, + 'iterations': 123, + 'salt': 'f00baa23', + 'next': '2t7b4g4vsa5smi47k61mv5bv1a22bojr', + 'windows': 'NS SOA MX RRSIG DNSKEY NSEC3PARAM', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3PARAM, '1 1 123 f00baa23'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'algorithm': 1, + 'flags': 1, + 'iterations': 123, + 'salt': 'f00baa23', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, 'example.com.'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'target': 'example.com.', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.RP, 'mbox-dname txt-dname'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'mbox': 'mbox-dname', + 'txt': 'txt-dname', + }, + ), + ( + dns.rdata.from_text( + dns.rdataclass.IN, + dns.rdatatype.RRSIG, + 'SOA 5 2 3600 20101127004331 20101119213831 61695 dnspython.org. sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw' + ' t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=', + ), + {'to_unicode': False, 'add_synthetic': False}, + { + 'type_covered': 'SOA', + 'algorithm': 5, + 'labels': 2, + 'original_ttl': 3600, + 'expiration': 1290818611, + 'inception': 1290202711, + 'key_tag': 61695, + 'signer': 'dnspython.org.', + 'signature': ( + 'sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDwt6dvM9jAXdIEi03l9H/RAd9xNNW6gv' + 'GMHsBGzpvvqFQxIBR2PoiZA1mX/SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd08vs=' + ), + }, + ), + ( + dns.rdata.from_text( + dns.rdataclass.IN, + dns.rdatatype.RRSIG, + 'NSEC 1 3 3600 20200101000000 20030101000000 2143 foo. MxFcby9k/yvedMfQgKzhH5er0Mu/vILz' + ' 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=', + ), + {'to_unicode': False, 'add_synthetic': False}, + { + 'type_covered': 'NSEC', + 'algorithm': 1, + 'labels': 3, + 'original_ttl': 3600, + 'expiration': 1577836800, + 'inception': 1041379200, + 'key_tag': 2143, + 'signer': 'foo.', + 'signature': 'MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY=', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.org. 1 7200 900 1209600 86400'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'mname': 'ns.example.com.', + 'rname': 'ns.example.org.', + 'serial': 1, + 'refresh': 7200, + 'retry': 900, + 'expire': 1209600, + 'minimum': 86400, + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SPF, '"v=spf1 a mx" " -all"'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'strings': [b'v=spf1 a mx', b' -all'], + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SPF, '"v=spf1 a mx" " -all"'), + {'to_unicode': False, 'add_synthetic': True}, + { + 'strings': [b'v=spf1 a mx', b' -all'], + 'value': b'v=spf1 a mx -all', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SRV, r'0 1 443 exchange.example.com'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'priority': 0, + 'weight': 1, + 'port': 443, + 'target': 'exchange.example.com', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SSHFP, r'1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'algorithm': 1, + 'fp_type': 1, + 'fingerprint': 'aa549bfe898489c02d1715d97d79c57ba2fa76ab', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TLSA, r'3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'usage': 3, + 'selector': 1, + 'mtype': 1, + 'cert': 'a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'strings': [b'asdf', b'foo bar'], + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'), + {'to_unicode': False, 'add_synthetic': True}, + { + 'strings': [b'asdf', b'foo bar'], + 'value': b'asdffoo bar', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'strings': [u'asdf', u'foo bar'], + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo bar"'), + {'to_unicode': True, 'add_synthetic': True}, + { + 'strings': [u'asdf', u'foo bar'], + 'value': u'asdffoo bar', + }, + ), +] + + +if dns.version.MAJOR >= 2: + # https://github.com/rthalley/dnspython/issues/321 makes this not working on dnspython < 2.0.0, + # which affects Python 3.5 and 2.x since these are only supported by dnspython < 2.0.0. + TEST_CONVERT_RDATA_TO_DICT.extend([ + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'), + {'to_unicode': False, 'add_synthetic': False}, + { + 'strings': [b'asdf', b'foo \xC3\xA4'], + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'), + {'to_unicode': False, 'add_synthetic': True}, + { + 'strings': [b'asdf', b'foo \xC3\xA4'], + 'value': b'asdffoo \xC3\xA4', + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'), + {'to_unicode': True, 'add_synthetic': False}, + { + 'strings': [u'asdf', u'foo ä'], + }, + ), + ( + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, r'asdf "foo \195\164"'), + {'to_unicode': True, 'add_synthetic': True}, + { + 'strings': [u'asdf', u'foo ä'], + 'value': u'asdffoo ä', + }, + ), + ]) + + +@pytest.mark.parametrize("rdata, kwarg, expected_result", TEST_CONVERT_RDATA_TO_DICT) +def test_convert_rdata_to_dict(rdata, kwarg, expected_result): + result = convert_rdata_to_dict(rdata, **kwarg) + print(expected_result) + print(result) + assert expected_result == result + + +def test_error(): + v = RDTYPE_TO_FIELDS.pop(dns.rdatatype.A) + try: + with pytest.raises(ValueError) as exc: + convert_rdata_to_dict(dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3')) + finally: + RDTYPE_TO_FIELDS[dns.rdatatype.A] = v + print(exc.value.args) + assert exc.value.args == ('Unsupported record type 1', ) diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py new file mode 100644 index 000000000..660f9adc1 --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_ips.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible_collections.community.dns.plugins.module_utils import ips + +from ansible_collections.community.dns.plugins.module_utils.ips import ( + assert_requirements_present, + is_ip_address, +) + +# We need ipaddress +ipaddress = pytest.importorskip('ipaddress') + + +def test_assert_requirements_present(): + class ModuleFailException(Exception): + pass + + class FakeModule(object): + def fail_json(self, **kwargs): + raise ModuleFailException(kwargs) + + module = FakeModule() + + orig_importerror = ips.IPADDRESS_IMPORT_EXC + try: + ips.IPADDRESS_IMPORT_EXC = None + assert_requirements_present(module) + + ips.IPADDRESS_IMPORT_EXC = 'asdf' + with pytest.raises(ModuleFailException) as exc: + assert_requirements_present(module) + + assert 'ipaddress' in exc.value.args[0]['msg'] + assert 'asdf' == exc.value.args[0]['exception'] + + finally: + ips.IPADDRESS_IMPORT_EXC = orig_importerror + + +IS_IP_ADDRESS_DATA = [ + ('foo.bar', False), + ('foo', False), + ('123', False), + ('1.2.3.4', True), + ('::', True), +] + + +@pytest.mark.parametrize("input, output", IS_IP_ADDRESS_DATA) +def test_is_ip_address(input, output): + assert is_ip_address(input) == output diff --git a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py index 1f51601e0..cea7fb1d6 100644 --- a/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py +++ b/ansible_collections/community/dns/tests/unit/plugins/module_utils/test_resolver.py @@ -36,24 +36,26 @@ def test_assert_requirements_present(): class ModuleFailException(Exception): pass - def fail_json(**kwargs): - raise ModuleFailException(kwargs) + class FakeModule(object): + def fail_json(self, **kwargs): + raise ModuleFailException(kwargs) - module = MagicMock() - module.fail_json = MagicMock(side_effect=fail_json) + module = FakeModule() orig_importerror = resolver.DNSPYTHON_IMPORTERROR - resolver.DNSPYTHON_IMPORTERROR = None - assert_requirements_present(module) - - resolver.DNSPYTHON_IMPORTERROR = 'asdf' - with pytest.raises(ModuleFailException) as exc: + try: + resolver.DNSPYTHON_IMPORTERROR = None assert_requirements_present(module) - assert 'dnspython' in exc.value.args[0]['msg'] - assert 'asdf' == exc.value.args[0]['exception'] + resolver.DNSPYTHON_IMPORTERROR = 'asdf' + with pytest.raises(ModuleFailException) as exc: + assert_requirements_present(module) + + assert 'dnspython' in exc.value.args[0]['msg'] + assert 'asdf' == exc.value.args[0]['exception'] - resolver.DNSPYTHON_IMPORTERROR = orig_importerror + finally: + resolver.DNSPYTHON_IMPORTERROR = orig_importerror def test_lookup_ns_names(): @@ -449,7 +451,69 @@ def test_timeout_failure(): assert exc.value.kwargs['timeout'] == 4 -def test_error_nxdomain(): +def test_error_nameserver_nxdomain_none(): + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers() + assert resolver.resolve_nameservers('example.com') == [] + + +def test_error_nameserver_nxdomain_partial_first(): + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers() + assert resolver.resolve_nameservers('example.com') == ['ns.com'] + + +def test_error_nameserver_nxdomain_partial_second(): resolver = mock_resolver(['1.1.1.1'], {}) udp_sequence = [ { @@ -461,14 +525,159 @@ def test_error_nxdomain(): }, 'result': create_mock_response(dns.rcode.NXDOMAIN), }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, ] with patch('dns.resolver.get_default_resolver', resolver): with patch('dns.resolver.Resolver', resolver): with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers() + assert resolver.resolve_nameservers('example.com') == ['ns.com'] + + +def test_error_nxdomain_ok(): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '2:3::4'), + )), + }, + ], + ('1:2::3', '2:3::4', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.com'), + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers() + rrset_dict = resolver.resolve('example.com', nxdomain_is_empty=True) + print(rrset_dict) + assert sorted(rrset_dict.keys()) == ['ns.com'] + assert rrset_dict['ns.com'] == [] + + +def test_error_nxdomain_fail(): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '2:3::4'), + )), + }, + ], + ('1:2::3', '2:3::4', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.com'), + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers() with pytest.raises(dns.resolver.NXDOMAIN) as exc: - resolver = ResolveDirectlyFromNameServers() - resolver.resolve_nameservers('example.com') - assert exc.value.kwargs['qnames'] == [dns.name.from_unicode(u'com')] + resolver.resolve('example.com', nxdomain_is_empty=False) + print(exc.value.args[0]) + assert exc.value.args[0] == 'The DNS query name does not exist: example.com.' def test_error_servfail(): @@ -493,6 +702,306 @@ def test_error_servfail(): assert exc.value.args[0] == 'Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."' +def test_error_servfail_retry_success(): + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers(servfail_retries=2) + assert resolver.resolve_nameservers('com') == ['ns.com'] + + +def test_error_servfail_retry_fail(): + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(ResolverError) as exc: + resolver = ResolveDirectlyFromNameServers(servfail_retries=2) + resolver.resolve_nameservers('example.com') + assert exc.value.args[0] == 'Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."' + + +def test_servfail_handling(): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '2.2.2.2'), + )), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers(servfail_retries=2) + assert resolver.resolve_nameservers('example.com', resolve_addresses=True) == ['3.3.3.3'] + # The following results should be cached: + assert resolver.resolve_nameservers('com') == ['ns.com'] + assert resolver.resolve_nameservers('com', resolve_addresses=True) == ['2.2.2.2'] + assert resolver.resolve_nameservers('example.com') == ['ns.example.com'] + assert resolver.resolve_nameservers('example.com', resolve_addresses=True) == ['3.3.3.3'] + + +def test_servfail_failing(): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '2.2.2.2'), + )), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + { + 'target': 'ns.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.SERVFAIL), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + resolver = ResolveDirectlyFromNameServers(servfail_retries=2) + assert resolver.resolve_nameservers('example.com', resolve_addresses=True) == ['3.3.3.3'] + # The following results should be cached: + assert resolver.resolve_nameservers('com') == ['ns.com'] + with pytest.raises(ResolverError) as exc: + resolver.resolve_nameservers('com', resolve_addresses=True) + assert exc.value.args[0] == "Error SERVFAIL while querying ['1.1.1.1']" + + def test_no_response(): fake_query = MagicMock() fake_query.question = 'Doctor Who?' diff --git a/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py new file mode 100644 index 000000000..e8fd4a6ed --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_info.py @@ -0,0 +1,395 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import MagicMock, patch + +from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import ( + set_module_args, + ModuleTestCase, + AnsibleExitJson, + AnsibleFailJson, +) + +from ansible_collections.community.dns.plugins.modules import nameserver_info + +from ..module_utils.resolver_helper import ( + mock_resolver, + mock_query_udp, + create_mock_answer, + create_mock_response, +) + +# We need dnspython +dns = pytest.importorskip('dns') + + +class TestNameserverInfo(ModuleTestCase): + def test_single(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + ), dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org') + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleExitJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + }) + nameserver_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['nameservers'] == [ + 'ns.example.com', + ] + + def test_single_ips(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + )), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + ), dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org') + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleExitJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + 'resolve_addresses': True, + }) + nameserver_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['nameservers'] == [ + '1:2::3', + '3.3.3.3', + ] + + def test_timeout(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'raise': dns.exception.Timeout(timeout=9), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'mail.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'raise': dns.exception.Timeout(timeout=9), + }, + { + 'query_target': dns.name.from_unicode(u'mail.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'raise': dns.exception.Timeout(timeout=9), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleFailJson) as exc: + set_module_args({ + 'name': ['www.example.com', 'mail.example.com'], + 'query_timeout': 9, + 'query_retry': 1, + }) + nameserver_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['msg'] in ( + 'Unexpected DNS error: The DNS operation timed out after 9 seconds', + 'Unexpected DNS error: The DNS operation timed out after 9.000 seconds', + ) + assert len(exc.value.args[0]['results']) == 2 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['nameservers'] == ['ns.example.com'] + assert exc.value.args[0]['results'][1]['name'] == 'mail.example.com' + assert 'nameservers' not in exc.value.args[0]['results'][1] + + def test_nxdomain(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + )), + }, + ], + ('1:2::3', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleExitJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + }) + nameserver_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['nameservers'] == ['ns.example.com'] + + def test_servfail(self): + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleFailJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + }) + nameserver_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['msg'] == 'Unexpected resolving error: Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."' + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert 'nameservers' not in exc.value.args[0]['results'][0] diff --git a/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py new file mode 100644 index 000000000..ee68586ff --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/modules/test_nameserver_record_info.py @@ -0,0 +1,592 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import MagicMock, patch + +from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import ( + set_module_args, + ModuleTestCase, + AnsibleExitJson, + AnsibleFailJson, +) + +from ansible_collections.community.dns.plugins.modules import nameserver_record_info + +from ..module_utils.resolver_helper import ( + mock_resolver, + mock_query_udp, + create_mock_answer, + create_mock_response, +) + +# We need dnspython +dns = pytest.importorskip('dns') + + +class TestNameserverRecordInfo(ModuleTestCase): + def test_single(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + )), + }, + { + 'target': 'ns.example.org', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '4.4.4.4'), + )), + }, + { + 'target': 'ns.example.org', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + ('1:2::3', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'asdf'), + )), + }, + ], + ('4.4.4.4', ): [ + { + 'target': dns.name.from_unicode(u'example.org'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'asdf'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'fdsa'), + )), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + ), dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org') + )]), + }, + { + 'query_target': dns.name.from_unicode(u'org'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'org', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.org'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.org'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.org', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.org'), + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleExitJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + 'type': 'TXT', + }) + nameserver_record_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert len(exc.value.args[0]['results'][0]['result']) == 2 + assert exc.value.args[0]['results'][0]['result'][0]['nameserver'] == 'ns.example.com' + assert exc.value.args[0]['results'][0]['result'][0]['values'] == [ + { + 'strings': ['asdf'], + 'value': 'asdf', + } + ] + assert exc.value.args[0]['results'][0]['result'][1]['nameserver'] == 'ns.example.org' + assert exc.value.args[0]['results'][0]['result'][1]['values'] == [ + { + 'strings': ['asdf'], + 'value': 'asdf', + }, + { + 'strings': ['fdsa'], + 'value': 'fdsa', + }, + ] + + def test_timeout(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 9, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 9, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + ('3.3.3.3', ): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 9, + 'raise': dns.exception.Timeout(timeout=9), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 9, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'www.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.TXT, 'fdsa asdf'), + )), + }, + { + 'target': dns.name.from_unicode(u'mail.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 9, + 'raise': dns.exception.Timeout(timeout=9), + }, + { + 'target': dns.name.from_unicode(u'mail.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 9, + 'raise': dns.exception.Timeout(timeout=9), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'mail.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 9, + }, + 'result': create_mock_response(dns.rcode.NOERROR, authority=[dns.rrset.from_rdata( + 'mail.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleFailJson) as exc: + set_module_args({ + 'name': ['www.example.com', 'mail.example.com'], + 'type': 'TXT', + 'query_timeout': 9, + 'query_retry': 1, + }) + nameserver_record_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['msg'] in ( + 'Unexpected DNS error: The DNS operation timed out after 9 seconds', + 'Unexpected DNS error: The DNS operation timed out after 9.000 seconds', + ) + assert len(exc.value.args[0]['results']) == 2 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['result'] == [ + { + 'nameserver': 'ns.example.com', + 'values': [ + { + 'strings': ['fdsa', 'asdf'], + 'value': 'fdsaasdf', + } + ], + } + ] + assert exc.value.args[0]['results'][1]['name'] == 'mail.example.com' + assert exc.value.args[0]['results'][1]['result'] == [] + + def test_nxdomain(self): + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + )), + }, + ], + ('1:2::3', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NXDOMAIN), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleExitJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + 'type': 'TXT', + }) + nameserver_record_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert len(exc.value.args[0]['results'][0]['result']) == 1 + assert exc.value.args[0]['results'][0]['result'][0]['nameserver'] == 'ns.example.com' + assert exc.value.args[0]['results'][0]['result'][0]['values'] == [] + + def test_servfail(self): + resolver = mock_resolver(['1.1.1.1'], {}) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.SERVFAIL), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleFailJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + 'type': 'TXT', + }) + nameserver_record_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['msg'] == 'Unexpected resolving error: Error SERVFAIL while querying 1.1.1.1 with query get NS for "com."' + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['result'] == [] + + def test_cname_loop(self): + fake_query = MagicMock() + fake_query.question = 'Doctor Who?' + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + { + 'target': 'ns.example.org', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.org', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '4.4.4.4'), + )), + }, + { + 'target': 'ns.example.org', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'raise': dns.resolver.NoAnswer(response=fake_query), + }, + ], + }) + udp_sequence = [ + { + 'query_target': dns.name.from_unicode(u'com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.example.com. ns.example.com. 12345 7200 120 2419200 10800'), + ), dns.rrset.from_rdata( + 'www.example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'example.org') + )]), + }, + { + 'query_target': dns.name.from_unicode(u'org'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'org', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.org'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'example.org'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.org', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.org'), + ), dns.rrset.from_rdata( + 'example.org', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.example.com') + )]), + }, + ] + with patch('dns.resolver.get_default_resolver', resolver): + with patch('dns.resolver.Resolver', resolver): + with patch('dns.query.udp', mock_query_udp(udp_sequence)): + with pytest.raises(AnsibleFailJson) as exc: + set_module_args({ + 'name': ['www.example.com'], + 'type': 'TXT', + }) + nameserver_record_info.main() + + print(exc.value.args[0]) + assert exc.value.args[0]['msg'] == 'Unexpected resolving error: Found CNAME loop starting at www.example.com' + assert len(exc.value.args[0]['results']) == 1 + assert exc.value.args[0]['results'][0]['name'] == 'www.example.com' + assert exc.value.args[0]['results'][0]['result'] == [] diff --git a/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py b/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py index 829d6465e..9ee935ba0 100644 --- a/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py +++ b/ansible_collections/community/dns/tests/unit/plugins/modules/test_wait_for_txt.py @@ -1195,7 +1195,44 @@ class TestWaitForTXT(ModuleTestCase): assert exc.value.args[0]['records'][1]['check_count'] == 1 def test_nxdomain(self): - resolver = mock_resolver(['1.1.1.1'], {}) + resolver = mock_resolver(['1.1.1.1'], { + ('1.1.1.1', ): [ + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.A, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '3.3.3.3'), + )), + }, + { + 'target': 'ns.example.com', + 'rdtype': dns.rdatatype.AAAA, + 'lifetime': 10, + 'result': create_mock_answer(dns.rrset.from_rdata( + 'ns.example.com', + 300, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.AAAA, '1:2::3'), + )), + }, + ], + ('1:2::3', '3.3.3.3'): [ + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + { + 'target': dns.name.from_unicode(u'www.example.com'), + 'rdtype': dns.rdatatype.TXT, + 'lifetime': 10, + 'result': create_mock_answer(rcode=dns.rcode.NXDOMAIN), + }, + ], + }) udp_sequence = [ { 'query_target': dns.name.from_unicode(u'com'), @@ -1207,7 +1244,20 @@ class TestWaitForTXT(ModuleTestCase): 'result': create_mock_response(dns.rcode.NXDOMAIN), }, { - 'query_target': dns.name.from_unicode(u'com'), + 'query_target': dns.name.from_unicode(u'example.com'), + 'query_type': dns.rdatatype.NS, + 'nameserver': '1.1.1.1', + 'kwargs': { + 'timeout': 10, + }, + 'result': create_mock_response(dns.rcode.NOERROR, answer=[dns.rrset.from_rdata( + 'example.com', + 3600, + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns.example.com'), + )]), + }, + { + 'query_target': dns.name.from_unicode(u'www.example.com'), 'query_type': dns.rdatatype.NS, 'nameserver': '1.1.1.1', 'kwargs': { @@ -1237,12 +1287,14 @@ class TestWaitForTXT(ModuleTestCase): wait_for_txt.main() print(exc.value.args[0]) + assert exc.value.args[0]['failed'] is True assert exc.value.args[0]['msg'] == 'Timeout (0 out of 1 check(s) passed).' assert exc.value.args[0]['completed'] == 0 assert len(exc.value.args[0]['records']) == 1 assert exc.value.args[0]['records'][0]['name'] == 'www.example.com' assert exc.value.args[0]['records'][0]['done'] is False - assert exc.value.args[0]['records'][0]['values'] == {} + assert len(exc.value.args[0]['records'][0]['values']) == 1 + assert exc.value.args[0]['records'][0]['values']['ns.example.com'] == [] assert exc.value.args[0]['records'][0]['check_count'] == 2 def test_servfail(self): diff --git a/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py new file mode 100644 index 000000000..211bad1bf --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_ips.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible.errors import AnsibleError + +from ansible_collections.community.dns.plugins.plugin_utils import ips + +from ansible_collections.community.dns.plugins.plugin_utils.ips import ( + assert_requirements_present, +) + +# We need ipaddress +ipaddress = pytest.importorskip('ipaddress') + + +def test_assert_requirements_present(): + orig_importerror = ips.IPADDRESS_IMPORT_EXC + try: + ips.IPADDRESS_IMPORT_EXC = None + assert_requirements_present('community.dns.foo', 'lookup') + + ips.IPADDRESS_IMPORT_EXC = Exception('asdf') + with pytest.raises(AnsibleError) as exc: + assert_requirements_present('community.dns.foo', 'lookup') + + assert 'ipaddress' in exc.value.args[0] + + finally: + ips.IPADDRESS_IMPORT_EXC = orig_importerror diff --git a/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py new file mode 100644 index 000000000..578155a68 --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/plugins/plugin_utils/test_resolver.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Felix Fontein <felix@fontein.de> +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible.errors import AnsibleError + +from ansible_collections.community.dns.plugins.plugin_utils import resolver + +from ansible_collections.community.dns.plugins.plugin_utils.resolver import ( + assert_requirements_present, +) + + +def test_assert_requirements_present(): + orig_importerror = resolver.DNSPYTHON_IMPORTERROR + try: + resolver.DNSPYTHON_IMPORTERROR = None + assert_requirements_present('community.dns.foo', 'lookup') + + resolver.DNSPYTHON_IMPORTERROR = Exception('asdf') + with pytest.raises(AnsibleError) as exc: + assert_requirements_present('community.dns.foo', 'lookup') + + assert 'dnspython' in exc.value.args[0] + + finally: + resolver.DNSPYTHON_IMPORTERROR = orig_importerror diff --git a/ansible_collections/community/dns/tests/unit/replace-requirements.sh b/ansible_collections/community/dns/tests/unit/replace-requirements.sh new file mode 100755 index 000000000..c3a52f5d1 --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/replace-requirements.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# 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 + +DIRECTORY="$(dirname "$0")" + +if [ -e "${DIRECTORY}/$1" ]; then + cp "${DIRECTORY}/$1" "${DIRECTORY}/requirements.txt" +fi diff --git a/ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt b/ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt new file mode 100644 index 000000000..126caabdc --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/requirements-stable-2.10.txt @@ -0,0 +1,13 @@ +# 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 + +unittest2 ; python_version < '2.7' +importlib ; python_version < '2.7' + +ipaddress ; python_version < '3.3' + +dnspython < 2.4.0 + +lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later +lxml ; python_version >= '2.7' diff --git a/ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt b/ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt new file mode 100644 index 000000000..126caabdc --- /dev/null +++ b/ansible_collections/community/dns/tests/unit/requirements-stable-2.9.txt @@ -0,0 +1,13 @@ +# 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 + +unittest2 ; python_version < '2.7' +importlib ; python_version < '2.7' + +ipaddress ; python_version < '3.3' + +dnspython < 2.4.0 + +lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later +lxml ; python_version >= '2.7' diff --git a/ansible_collections/community/dns/tests/unit/requirements.txt b/ansible_collections/community/dns/tests/unit/requirements.txt index 8b7c7cd61..dff61418a 100644 --- a/ansible_collections/community/dns/tests/unit/requirements.txt +++ b/ansible_collections/community/dns/tests/unit/requirements.txt @@ -5,6 +5,8 @@ unittest2 ; python_version < '2.7' importlib ; python_version < '2.7' +ipaddress ; python_version < '3.3' + dnspython lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later |