From 686aec7ae3d6649231211c47bfae7b3fba435d75 Mon Sep 17 00:00:00 2001 From: juju4 Date: Sun, 19 Jan 2025 00:07:43 +0000 Subject: [PATCH 1/6] Add role for dns_exporter Signed-off-by: juju4 --- roles/dns_exporter/README.md | 55 ++++ roles/dns_exporter/TROUBLESHOOTING.md | 1 + roles/dns_exporter/defaults/main.yml | 32 +++ roles/dns_exporter/handlers/main.yml | 10 + roles/dns_exporter/meta/argument_specs.yml | 84 ++++++ roles/dns_exporter/meta/main.yml | 25 ++ .../molecule/alternative/molecule.yml | 26 ++ .../alternative/tests/test_alternative.py | 43 +++ .../molecule/default/molecule.yml | 6 + .../molecule/default/tests/test_default.py | 76 ++++++ .../dns_exporter/molecule/latest/molecule.yml | 7 + .../molecule/latest/tests/test_latest.py | 51 ++++ roles/dns_exporter/tasks/configure.yml | 30 +++ roles/dns_exporter/tasks/main.yml | 83 ++++++ roles/dns_exporter/tasks/preflight.yml | 65 +++++ .../templates/dns_exporter.service.j2 | 101 ++++++++ .../templates/dns_exporter.yml.j2 | 245 ++++++++++++++++++ roles/dns_exporter/test-requirements.txt | 1 + roles/dns_exporter/vars/main.yml | 9 + 19 files changed, 950 insertions(+) create mode 100644 roles/dns_exporter/README.md create mode 100644 roles/dns_exporter/TROUBLESHOOTING.md create mode 100644 roles/dns_exporter/defaults/main.yml create mode 100644 roles/dns_exporter/handlers/main.yml create mode 100644 roles/dns_exporter/meta/argument_specs.yml create mode 100644 roles/dns_exporter/meta/main.yml create mode 100644 roles/dns_exporter/molecule/alternative/molecule.yml create mode 100644 roles/dns_exporter/molecule/alternative/tests/test_alternative.py create mode 100644 roles/dns_exporter/molecule/default/molecule.yml create mode 100644 roles/dns_exporter/molecule/default/tests/test_default.py create mode 100644 roles/dns_exporter/molecule/latest/molecule.yml create mode 100644 roles/dns_exporter/molecule/latest/tests/test_latest.py create mode 100644 roles/dns_exporter/tasks/configure.yml create mode 100644 roles/dns_exporter/tasks/main.yml create mode 100644 roles/dns_exporter/tasks/preflight.yml create mode 100644 roles/dns_exporter/templates/dns_exporter.service.j2 create mode 100644 roles/dns_exporter/templates/dns_exporter.yml.j2 create mode 100644 roles/dns_exporter/test-requirements.txt create mode 100644 roles/dns_exporter/vars/main.yml diff --git a/roles/dns_exporter/README.md b/roles/dns_exporter/README.md new file mode 100644 index 000000000..cc36301b8 --- /dev/null +++ b/roles/dns_exporter/README.md @@ -0,0 +1,55 @@ +# Ansible Role: dns exporter + +## Description + +Deploy prometheus [dns exporter](https://github.com/tykling/dns_exporter), written in python, using ansible. + +Possible Grafana dashboard: https://grafana.com/grafana/dashboards/20617-dns-exporter/ + +## Requirements + +- Ansible >= 2.9 (It might work on previous versions, but we cannot guarantee it) +- gnu-tar on Mac deployer host (`brew install gnu-tar`) +- Passlib is required when using the basic authentication feature (`pip install passlib[bcrypt]`) + +## Role Variables + +All variables which can be overridden are stored in [defaults/main.yml](defaults/main.yml) file as well as in [meta/argument_specs.yml](meta/argument_specs.yml). +Please refer to the [collection docs](https://prometheus-community.github.io/ansible/branch/main/dns_exporter_role.html) for description and default values of the variables. + +## Example + +### Playbook + +Use it in a playbook as follows: +```yaml +- hosts: all + roles: + - prometheus.prometheus.dns_exporter +``` + +## Local Testing + +The preferred way of locally testing the role is to use Docker and [molecule](https://github.com/ansible-community/molecule) (v3.x). You will have to install Docker on your system. See "Get started" for a Docker package suitable for your system. Running your tests is as simple as executing `molecule test`. + +## Continuous Integration + +Combining molecule and circle CI allows us to test how new PRs will behave when used with multiple ansible versions and multiple operating systems. This also allows use to create test scenarios for different role configurations. As a result we have quite a large test matrix which can take more time than local testing, so please be patient. + +## Contributing + +See [contributor guideline](CONTRIBUTING.md). + +## Troubleshooting + +See [troubleshooting](TROUBLESHOOTING.md). + +Manual test +``` +curl -v "http://127.0.0.1:15353/query?query_name=gmail.com&server=127.0.0.2:53" +curl -v "http://127.0.0.1:15353/query?module=internal_a_v4&module=internalbind_a_v4&module=internaldnscrypt_a_v4&query_name=youtube.com" +``` + +## License + +This project is licensed under MIT License. See [LICENSE](/LICENSE) for more details. diff --git a/roles/dns_exporter/TROUBLESHOOTING.md b/roles/dns_exporter/TROUBLESHOOTING.md new file mode 100644 index 000000000..4f341277d --- /dev/null +++ b/roles/dns_exporter/TROUBLESHOOTING.md @@ -0,0 +1 @@ +# Troubleshooting diff --git a/roles/dns_exporter/defaults/main.yml b/roles/dns_exporter/defaults/main.yml new file mode 100644 index 000000000..fcca3769e --- /dev/null +++ b/roles/dns_exporter/defaults/main.yml @@ -0,0 +1,32 @@ +--- +dns_exporter_version: 1.0.0 +# not go, but python pip. placeholders for _common +dns_exporter_binary_url: "" +dns_exporter_checksums_url: "" +dns_exporter_textfile_dir: "" +dns_exporter_enabled_collectors: [] +dns_exporter_disabled_collectors: [] +dns_exporter_tls_server_config: {} +dns_exporter_http_server_config: {} +dns_exporter_basic_auth_users: {} +# just for "prometheus.prometheus._common : Naive assertion of proper listen address" else not used +dns_exporter_web_listen_address: "127.0.0.1:15353" + +dns_exporter_web_disable_exporter_metrics: false +dns_exporter_web_listen_ip: "127.0.0.1" +dns_exporter_web_listen_port: "15353" +dns_exporter_web_telemetry_path: "/metrics" + +dns_exporter_debug_enable: false + +dns_exporter_home: "/var/lib/dns_exporter" +dns_exporter_virtualenv: "/var/lib/dns_exporter/venv" + +dns_exporter_config_template: dns_exporter.yml.j2 + +dns_exporter_system_group: "dns-exp" +dns_exporter_system_user: "{{ dns_exporter_system_group }}" + +dns_exporter_config_dir: "/etc/dns_exporter" +# Local path to stash the archive and its extraction +dns_exporter_local_cache_path: "/tmp/dns_exporter-{{ ansible_system | lower }}-{{ _dns_exporter_go_ansible_arch }}/{{ dns_exporter_version }}" diff --git a/roles/dns_exporter/handlers/main.yml b/roles/dns_exporter/handlers/main.yml new file mode 100644 index 000000000..165703b38 --- /dev/null +++ b/roles/dns_exporter/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: Restart dns_exporter + listen: "restart dns_exporter" + become: true + ansible.builtin.systemd: + daemon_reload: true + name: dns_exporter + state: restarted + when: + - not ansible_check_mode diff --git a/roles/dns_exporter/meta/argument_specs.yml b/roles/dns_exporter/meta/argument_specs.yml new file mode 100644 index 000000000..3e0df6d8c --- /dev/null +++ b/roles/dns_exporter/meta/argument_specs.yml @@ -0,0 +1,84 @@ +--- +# yamllint disable rule:line-length +argument_specs: + main: + short_description: "Prometheus DNS Exporter" + description: + - "Deploy prometheus L(dns exporter,https://github.com/tykling/dns_exporter) using ansible" + author: + - "Prometheus Community" + options: + dns_exporter_version: + description: "DNS exporter package version. Also accepts latest as parameter." + default: "1.0.0" + dns_exporter_binary_url: + description: "URL of the dns exporter binaries .tar.gz file" + default: "https://github.com/{{ _dns_exporter_repo }}/releases/download/v{{ dns_exporter_version }}/dns_exporter-{{ dns_exporter_version }}.{{ ansible_system | lower }}-{{ _dns_exporter_go_ansible_arch }}.tar.gz" + dns_exporter_checksums_url: + description: "URL of the dns exporter checksums file" + default: "https://github.com/{{ _dns_exporter_repo }}/releases/download/v{{ dns_exporter_version }}/sha256sums.txt" + dns_exporter_web_disable_exporter_metrics: + description: "Exclude metrics about the exporter itself (promhttp_*, process_*, go_*)." + type: bool + default: false + dns_exporter_web_listen_address: + description: "Address on which dns exporter will listen" + default: "0.0.0.0:15353" + dns_exporter_web_telemetry_path: + description: "Path under which to expose metrics" + default: "/metrics" + dns_exporter_enabled_collectors: + description: + - "List of dicts defining additionally enabled collectors and their configuration." + - "It adds collectors to L(those enabled by default,https://github.com/prometheus/dns_exporter#enabled-by-default)." + type: "list" + default: + - systemd + - textfile: + directory: "{{ dns_exporter_textfile_dir }}" + dns_exporter_disabled_collectors: + description: + - "List of disabled collectors." + - "By default dns_exporter disables collectors listed L(here,https://github.com/prometheus/dns_exporter#disabled-by-default)." + type: "list" + elements: "str" + dns_exporter_textfile_dir: + description: + - "Directory used by the L(Textfile Collector,https://github.com/prometheus/dns_exporter#textfile-collector)." + - "To get permissions to write metrics in this directory, users must be in C(dns-exp) system group." + - "B(Note:) More information in TROUBLESHOOTING.md guide." + default: "/var/lib/dns_exporter" + dns_exporter_tls_server_config: + description: + - "Configuration for TLS authentication." + - "Keys and values are the same as in L(dns_exporter docs,https://prometheus.io/docs/prometheus/latest/configuration/https/)." + type: "dict" + dns_exporter_http_server_config: + description: + - "Config for HTTP/2 support." + - "Keys and values are the same as in L(dns_exporter docs,https://prometheus.io/docs/prometheus/latest/configuration/https/)." + type: "dict" + dns_exporter_basic_auth_users: + description: "Dictionary of users and password for basic authentication. Passwords are automatically hashed with bcrypt." + type: "dict" + dns_exporter_binary_install_dir: + description: + - "I(Advanced)" + - "Directory to install dns_exporter binary" + default: "/usr/local/bin" + dns_exporter_system_group: + description: + - "I(Advanced)" + - "System group for dns exporter" + default: "dns-exp" + dns_exporter_system_user: + description: + - "I(Advanced)" + - "DNS exporter user" + default: "dns-exp" + dns_exporter_local_cache_path: + description: "Local path to stash the archive and its extraction" + default: "/tmp/dns_exporter-{{ ansible_system | lower }}-{{ _dns_exporter_go_ansible_arch }}/{{ dns_exporter_version }}" + dns_exporter_config_dir: + description: "Path to directory with dns_exporter configuration" + default: "/etc/dns_exporter" diff --git a/roles/dns_exporter/meta/main.yml b/roles/dns_exporter/meta/main.yml new file mode 100644 index 000000000..a2e6be580 --- /dev/null +++ b/roles/dns_exporter/meta/main.yml @@ -0,0 +1,25 @@ +--- +galaxy_info: + author: "Prometheus Community" + description: "Prometheus DNS Exporter" + license: "Apache" + min_ansible_version: "2.9" + platforms: + - name: "Ubuntu" + versions: + - "focal" + - "jammy" + - "noble" + - name: "Debian" + versions: + - "bullseye" + - name: "EL" + versions: + - "8" + - "9" + galaxy_tags: + - "monitoring" + - "prometheus" + - "exporter" + - "metrics" + - "system" diff --git a/roles/dns_exporter/molecule/alternative/molecule.yml b/roles/dns_exporter/molecule/alternative/molecule.yml new file mode 100644 index 000000000..333719833 --- /dev/null +++ b/roles/dns_exporter/molecule/alternative/molecule.yml @@ -0,0 +1,26 @@ +--- +provisioner: + playbooks: + prepare: "${MOLECULE_PROJECT_DIRECTORY}/../../.config/molecule/alternative/prepare.yml" + inventory: + group_vars: + all: + dns_exporter_local_cache_path: "/tmp/dns_exporter-linux-amd64" + dns_exporter_web_listen_address: + - '127.0.0.1:8080' + - '127.0.1.1:8080' + dns_exporter_textfile_dir: "" + dns_exporter_enabled_collectors: + - entropy + dns_exporter_disabled_collectors: + - diskstats + dns_exporter_tls_server_config: + cert_file: /etc/dns_exporter/tls.cert + key_file: /etc/dns_exporter/tls.key + dns_exporter_http_server_config: + http2: true + dns_exporter_basic_auth_users: + randomuser: examplepassword + dns_exporter_version: 1.5.0 + dns_exporter_binary_url: "https://github.com/prometheus/dns_exporter/releases/download/v{{\ + \ dns_exporter_version }}/dns_exporter-{{ dns_exporter_version }}.linux-amd64.tar.gz" diff --git a/roles/dns_exporter/molecule/alternative/tests/test_alternative.py b/roles/dns_exporter/molecule/alternative/tests/test_alternative.py new file mode 100644 index 000000000..ade010e3e --- /dev/null +++ b/roles/dns_exporter/molecule/alternative/tests/test_alternative.py @@ -0,0 +1,43 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from testinfra_helpers import get_target_hosts +import pytest + +testinfra_hosts = get_target_hosts() + + +def test_directories(host): + dirs = [ + "/var/lib/dns_exporter" + ] + for dir in dirs: + d = host.file(dir) + assert not d.exists + + +def test_service(host): + s = host.service("dns_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u dns_exporter --since "1 hour ago"') + print("\n==== journalctl -u dns_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_protecthome_property(host): + s = host.service("dns_exporter") + p = s.systemd_properties + assert p.get("ProtectHome") == "yes" + + +@pytest.mark.parametrize("sockets", [ + "tcp://127.0.0.1:8080", + "tcp://127.0.1.1:8080", +]) +def test_socket(host, sockets): + assert host.socket(sockets).is_listening diff --git a/roles/dns_exporter/molecule/default/molecule.yml b/roles/dns_exporter/molecule/default/molecule.yml new file mode 100644 index 000000000..8362c1413 --- /dev/null +++ b/roles/dns_exporter/molecule/default/molecule.yml @@ -0,0 +1,6 @@ +--- +provisioner: + inventory: + group_vars: + all: + dns_exporter_web_listen_address: "127.0.0.1:15353" diff --git a/roles/dns_exporter/molecule/default/tests/test_default.py b/roles/dns_exporter/molecule/default/tests/test_default.py new file mode 100644 index 000000000..23a8854a3 --- /dev/null +++ b/roles/dns_exporter/molecule/default/tests/test_default.py @@ -0,0 +1,76 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from testinfra_helpers import get_target_hosts + +testinfra_hosts = get_target_hosts() + + +def test_directories(host): + dirs = [ + "/var/lib/dns_exporter" + ] + for dir in dirs: + d = host.file(dir) + assert d.is_directory + assert d.exists + + +def test_files(host): + files = [ + "/etc/systemd/system/dns_exporter.service", + "/usr/local/bin/dns_exporter" + ] + for file in files: + f = host.file(file) + assert f.exists + assert f.is_file + + +def test_permissions_didnt_change(host): + dirs = [ + "/etc", + "/root", + "/usr", + "/var" + ] + for file in dirs: + f = host.file(file) + assert f.exists + assert f.is_directory + assert f.user == "root" + assert f.group == "root" + + +def test_user(host): + assert host.group("dns-exp").exists + assert "dns-exp" in host.user("dns-exp").groups + assert host.user("dns-exp").shell == "/usr/sbin/nologin" + + +def test_service(host): + s = host.service("dns_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u dns_exporter --since "1 hour ago"') + print("\n==== journalctl -u dns_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_protecthome_property(host): + s = host.service("dns_exporter") + p = s.systemd_properties + assert p.get("ProtectHome") == "yes" + + +def test_socket(host): + sockets = [ + "tcp://127.0.0.1:15353" + ] + for socket in sockets: + s = host.socket(socket) + assert s.is_listening diff --git a/roles/dns_exporter/molecule/latest/molecule.yml b/roles/dns_exporter/molecule/latest/molecule.yml new file mode 100644 index 000000000..cc9586f62 --- /dev/null +++ b/roles/dns_exporter/molecule/latest/molecule.yml @@ -0,0 +1,7 @@ +--- +provisioner: + inventory: + group_vars: + all: + dns_exporter_version: latest + dns_exporter_textfile_dir: /home/dns_exporter diff --git a/roles/dns_exporter/molecule/latest/tests/test_latest.py b/roles/dns_exporter/molecule/latest/tests/test_latest.py new file mode 100644 index 000000000..3ef5674d9 --- /dev/null +++ b/roles/dns_exporter/molecule/latest/tests/test_latest.py @@ -0,0 +1,51 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from testinfra_helpers import get_target_hosts +import pytest + +testinfra_hosts = get_target_hosts() + + +@pytest.mark.parametrize("files", [ + "/etc/systemd/system/dns_exporter.service", + "/usr/local/bin/dns_exporter" +]) +def test_files(host, files): + f = host.file(files) + assert f.exists + assert f.is_file + + +def test_directories(host): + dirs = [ + "/home/dns_exporter" + ] + for dir in dirs: + d = host.file(dir) + assert d.is_directory + assert d.exists + + +def test_service(host): + s = host.service("dns_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u dns_exporter --since "1 hour ago"') + print("\n==== journalctl -u dns_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_protecthome_property(host): + s = host.service("dns_exporter") + p = s.systemd_properties + assert p.get("ProtectHome") == "read-only" + + +def test_socket(host): + s = host.socket("tcp://0.0.0.0:15353") + assert s.is_listening diff --git a/roles/dns_exporter/tasks/configure.yml b/roles/dns_exporter/tasks/configure.yml new file mode 100644 index 000000000..8800ca9f3 --- /dev/null +++ b/roles/dns_exporter/tasks/configure.yml @@ -0,0 +1,30 @@ +--- +- name: Configure + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: configure.yml + vars: + _common_system_user: "{{ dns_exporter_system_user }}" + _common_system_group: "{{ dns_exporter_system_group }}" + _common_config_dir: "{{ dns_exporter_config_dir }}" + _common_tls_server_config: "{{ dns_exporter_tls_server_config }}" + _common_http_server_config: "{{ dns_exporter_http_server_config }}" + _common_basic_auth_users: "{{ dns_exporter_basic_auth_users }}" + tags: + - dns_exporter + - configure + - dns_exporter_configure + +- name: Create textfile collector dir + ansible.builtin.file: + path: "{{ dns_exporter_textfile_dir }}" + state: directory + owner: "{{ dns_exporter_system_user }}" + group: "{{ dns_exporter_system_group }}" + mode: u+rwX,g+rwX,o=rX + become: true + when: dns_exporter_textfile_dir | length > 0 + tags: + - dns_exporter + - configure + - dns_exporter_configure diff --git a/roles/dns_exporter/tasks/main.yml b/roles/dns_exporter/tasks/main.yml new file mode 100644 index 000000000..77139702d --- /dev/null +++ b/roles/dns_exporter/tasks/main.yml @@ -0,0 +1,83 @@ +--- +- name: Preflight + ansible.builtin.include_tasks: + file: preflight.yml + tags: + - dns_exporter_install + - dns_exporter_configure + - dns_exporter_run + +- name: Install + tags: + - dns_exporter_install + block: + - name: Install packages dependencies + ansible.builtin.package: + name: + - python3 + - python3-pip + - python3-virtualenv + state: present + - name: "Create system group {{ dns_exporter_system_group }}" + ansible.builtin.group: + name: "{{ dns_exporter_system_group }}" + state: present + - name: "Create system user {{ dns_exporter_system_user }}" + ansible.builtin.user: + name: "{{ dns_exporter_system_user }}" + system: true + shell: "/usr/sbin/nologin" + group: "{{ dns_exporter_system_group }}" + home: "{{ dns_exporter_home }}" + create_home: true + become: true + - name: Install dns_exporter from pip + ansible.builtin.pip: + name: dns_exporter + version: "{{ dns_exporter_version }}" + state: present + virtualenv: "{{ dns_exporter_virtualenv }}" + become: true + become_user: "{{ dns_exporter_system_user }}" + - name: Ensure config dir exists + ansible.builtin.file: + path: "{{ dns_exporter_config_dir }}" + state: directory + mode: '0775' # like _common + owner: root + - name: Configure dns_exporter + ansible.builtin.template: + src: "{{ dns_exporter_config_template }}" + dest: "{{ dns_exporter_config_dir }}/dns_exporter.yml" + mode: '0644' + owner: root + notify: + - Restart dns_exporter + +- name: SELinux + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: selinux.yml + vars: + _common_selinux_port: "{{ dns_exporter_web_listen_address | urlsplit('port') }}" + when: ansible_selinux.status == "enabled" + tags: + - dns_exporter_configure + +- name: Configure + ansible.builtin.include_tasks: + file: configure.yml + tags: + - dns_exporter_configure + +- name: Ensure DNS Exporter is enabled on boot + become: true + ansible.builtin.systemd: + daemon_reload: true + name: dns_exporter + enabled: true + state: started + when: + - not ansible_check_mode + tags: + - dns_exporter_run diff --git a/roles/dns_exporter/tasks/preflight.yml b/roles/dns_exporter/tasks/preflight.yml new file mode 100644 index 000000000..0ddecad0f --- /dev/null +++ b/roles/dns_exporter/tasks/preflight.yml @@ -0,0 +1,65 @@ +--- +- name: Common preflight + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: preflight.yml + vars: + _common_web_listen_address: "{{ dns_exporter_web_listen_address }}" + +- name: Assert that used version supports listen address type + ansible.builtin.assert: + that: + - >- + dns_exporter_web_listen_address is string + or + ( + dns_exporter_version is version('1.5.0', '>=') and + dns_exporter_web_listen_address | type_debug == "list" + ) + +- name: Assert collectors are not both disabled and enabled at the same time + ansible.builtin.assert: + that: + - "item not in dns_exporter_enabled_collectors" + with_items: "{{ dns_exporter_disabled_collectors }}" + +- name: Assert that TLS config is correct + when: dns_exporter_tls_server_config | length > 0 + block: + - name: Assert that TLS key and cert path are set + ansible.builtin.assert: + that: + - "dns_exporter_tls_server_config.cert_file is defined" + - "dns_exporter_tls_server_config.key_file is defined" + + - name: Check existence of TLS cert file + ansible.builtin.stat: + path: "{{ dns_exporter_tls_server_config.cert_file }}" + register: __dns_exporter_cert_file + + - name: Check existence of TLS key file + ansible.builtin.stat: + path: "{{ dns_exporter_tls_server_config.key_file }}" + register: __dns_exporter_key_file + + - name: Assert that TLS key and cert are present + ansible.builtin.assert: + that: + - "__dns_exporter_cert_file.stat.exists" + - "__dns_exporter_key_file.stat.exists" + +- name: Discover latest version + ansible.builtin.set_fact: + dns_exporter_version: "{{ (lookup('url', 'https://api.github.com/repos/{{ _dns_exporter_repo }}/releases/latest', headers=_github_api_headers, + split_lines=False) | from_json).get('tag_name') | replace('v', '') }}" + run_once: true + until: dns_exporter_version is version('0.0.0', '>=') + retries: 10 + when: + - dns_exporter_version == "latest" + tags: + - dns_exporter + - install + - dns_exporter_install + - download + - dns_exporter_download diff --git a/roles/dns_exporter/templates/dns_exporter.service.j2 b/roles/dns_exporter/templates/dns_exporter.service.j2 new file mode 100644 index 000000000..6eed816d1 --- /dev/null +++ b/roles/dns_exporter/templates/dns_exporter.service.j2 @@ -0,0 +1,101 @@ +{{ ansible_managed | comment }} + +# Exposure level `systemd-analyze security`: 2.7 OK + +[Unit] +Description=Prometheus DNS Exporter +After=network-online.target + +[Service] +Type=simple +User={{ dns_exporter_system_user }} +Group={{ dns_exporter_system_group }} +ExecStart={{ dns_exporter_virtualenv }}/bin/dns_exporter \ + -L {{ dns_exporter_web_listen_ip }} \ + -p {{ dns_exporter_web_listen_port }} \ + -c {{ dns_exporter_config_dir }}/dns_exporter.yml \ + {% if dns_exporter_debug_enable | bool %}-d{% endif %} + + +SyslogIdentifier=dns_exporter +Restart=always +RestartSec=1 +StartLimitInterval=0 + +{% set ns = namespace(protect_home = 'yes') %} +{% for m in ansible_mounts if m.mount.startswith('/home') %} +{% set ns.protect_home = 'read-only' %} +{% endfor %} +{% if dns_exporter_textfile_dir.startswith('/home') %} +{% set ns.protect_home = 'read-only' %} +{% endif %} +ProtectHome={{ ns.protect_home }} +NoNewPrivileges=yes +PrivateTmp=true +UMask=077 + +{% if (ansible_facts.packages.systemd | first).version is version('232', '>=') %} +ProtectSystem=strict +ProtectProc=noaccess +ProtectControlGroups=true +ProtectKernelModules=true +ProtectKernelTunables=yes +ProtectHostname=yes +ProtectClock=yes +LockPersonality=true +RestrictRealtime=true +RestrictNamespaces=yes +RestrictSUIDSGID=yes +MemoryDenyWriteExecute=yes +RemoveIPC=yes +{% else %} +ProtectSystem=full +{% endif %} + +IPAccounting=yes +IPAddressAllow=localhost link-local multicast 10.0.0.0/8 192.168.0.0/16 +# IPAddressDeny= + +CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_DAC_READ_SEARCH +CapabilityBoundingSet=~CAP_BLOCK_SUSPEND +CapabilityBoundingSet=~CAP_BPF +CapabilityBoundingSet=~CAP_DAC_* CAP_FOWNER CAP_IPC_OWNER +CapabilityBoundingSet=~CAP_FSETID CAP_SETFCAP +CapabilityBoundingSet=~CAP_IPC_LOCK +CapabilityBoundingSet=~CAP_KILL +CapabilityBoundingSet=~CAP_LEASE +CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE +CapabilityBoundingSet=~CAP_MKNOD +CapabilityBoundingSet=~CAP_NET_ADMIN +CapabilityBoundingSet=~CAP_NET_BIND_SERVICE CAP_NET_BROADCAST +CapabilityBoundingSet=~CAP_NET_RAW +CapabilityBoundingSet=~CAP_SETUID CAP_SETGID CAP_SETPCAP +CapabilityBoundingSet=~CAP_SYS_CHROOT +CapabilityBoundingSet=~CAP_SYS_PACCT +CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG +CapabilityBoundingSet=~CAP_SYS_ADMIN +CapabilityBoundingSet=~CAP_SYS_NICE CAP_SYS_RESOURCE +CapabilityBoundingSet=~CAP_SYS_BOOT +CapabilityBoundingSet=~CAP_SYS_RAWIO +CapabilityBoundingSet=~CAP_SYS_PTRACE + +{% if not (ansible_virtualization_type is defined and + (ansible_virtualization_type == "lxc" or ansible_virtualization_type == "docker") + ) +%} +SystemCallFilter=@system-service @privileged @resources +{% endif %} +SystemCallFilter=~@clock @cpu-emulation @debug @mount @obsolete @privileged @raw-io @reboot @resources @swap @module +SystemCallArchitectures=native +# When system call is disallowed, return error code instead of killing process +SystemCallErrorNumber=EPERM + +{% if dns_exporter_cgroups_restriction_enable is defined and dns_exporter_cgroups_restriction_enable|bool %} +CPUWeight={{ dns_exporter_cgroups_cpushares | default('80') }} +CPUQuota={{ dns_exporter_cgroups_cpuquota | default('40%') }} +MemoryMax={{ dns_exporter_cgroups_memorylimit | default('2G') }} +IOWeight={{ dns_exporter_cgroups_ioweight | default('80') }} +{% endif %} + +[Install] +WantedBy=multi-user.target diff --git a/roles/dns_exporter/templates/dns_exporter.yml.j2 b/roles/dns_exporter/templates/dns_exporter.yml.j2 new file mode 100644 index 000000000..17a590fef --- /dev/null +++ b/roles/dns_exporter/templates/dns_exporter.yml.j2 @@ -0,0 +1,245 @@ +--- +{{ ansible_managed | comment }} + +# https://github.com/tykling/dns_exporter/blob/main/src/dns_exporter/dns_exporter_example.yml + +snippets: # this name is not important + ipv4_2s: &template_v4_2s + timeout: "2" + family: "ipv4" + +more_reusable_stuff: # the name of this dict is also not important + mytemplate: &mytemplate + timeout: "2" # query timeout in seconds + protocol: "udp" # one of udp, tcp, dot, doh, doq; all these can also be set in scrape request + valid_rcodes: + - "NOERROR" # all RCODEs in the list will be considered valid + query_name: "example.com" # can also be set in scrape request + query_type: "A" # can also be set in scrape request + query_class: "IN" # can also be set in scrape request + recursion_desired: true # set False e.g. to test caching on recursors + + slowtemplate: &othertemplate + <<: *mytemplate + timeout: "10s" + + v4_example: + <<: *mytemplate + family: "ipv4" # when scrape server is a hostname use ipv4/A instead of AAAA + + validation_kitchensink_example: &kitchensink + <<: *mytemplate + validate_response_flags: # do validation of the response flags + fail_if_any_present: # consider request failed if any of these flags are present in the response + - "AA" + fail_if_all_present: # consider request failed if all these flags are present in the response + - "QR" + - "AA" + - "RD" + - "RA" + fail_if_any_absent: # consider request failed if any of these flags are absent from the response + - "AD" + fail_if_all_absent: # consider request failed if any of these flags are absent from the response + - "AD" + - "AA" + validate_answer_rrs: # do validation of response ANSWER rr + fail_if_matches_regexp: # consider request failed if any answer rr matches one of these regexes + - ".*127.0.0.1" + fail_if_all_match_regexp: # consider request failed if all answer rrs match one of these regexes + - ".*127.0.0.1" + fail_if_not_matches_regexp: # consider request failed if any answer rr does not match one of these regexes + - "www.prometheus.io.\t300\tIN\tA\t127.0.0.1" + fail_if_none_matches_regexp: # consider request failed if none of the answer rrs match one of these regexes + - ".*127.0.0.1" + fail_if_count_eq: 5 # fail if rr count equals 5 + fail_if_count_ne: 13 # fail if rr count does not equal 13 + fail_if_count_lt: 2 # fail if rr count is smaller than 2 + fail_if_count_gt: 3 # fail if rr count is larger than 3 + validate_authority_rrs: # do validation of response AUTHORITY rr + fail_if_matches_regexp: + - ".*127.0.0.1" + validate_additional_rrs: # do validation of response ADDITIONAL rr + fail_if_matches_regexp: + - ".*127.0.0.1" + + +modules: # this is the only key currently read from the config + + udp: *template_v4_2s + + tcp: + <<: *template_v4_2s + protocol: "tcp" + + ipv4: + <<: *template_v4_2s + family: "ipv4" + + ipv6: + <<: *template_v4_2s + family: "ipv6" + + dot: + <<: *template_v4_2s + protocol: "dot" + + doh: + <<: *template_v4_2s + protocol: "doh" + + doq: + <<: *template_v4_2s + protocol: "doq" + + soa: + <<: *template_v4_2s + query_type: "SOA" + + any: + <<: *template_v4_2s + query_type: "ANY" + + chaos: + <<: *template_v4_2s + query_class: "CHAOS" + + cache: + <<: *template_v4_2s + recursion_desired: false + + fast: + <<: *template_v4_2s + timeout: "0.5" + + forceip: + <<: *template_v4_2s + ip: "192.0.2.53" + + server: + <<: *template_v4_2s + server: "doh.example.com" + + noedns: + <<: *template_v4_2s + edns: false + + dnssecok: + <<: *template_v4_2s + edns_do: true + + nsid: + <<: *template_v4_2s + edns_nsid: true + + large: + <<: *template_v4_2s + edns_bufsize: 4000 + + pad: + <<: *template_v4_2s + edns_pad: 100 + + kitchensink: + <<: *kitchensink + + google_udp: # make sure google public dns returns 13 NS records for . over UDP + protocol: "udp" + server: "dns.google" + query_name: "." + query_type: "NS" + validate_answer_rrs: + fail_if_count_ne: 13 + + google_tcp: # make sure google public dns returns 2 A records for dns.google over TCP + protocol: "tcp" + server: "dns.google" + query_name: "dns.google" + query_type: "A" + validate_answer_rrs: + fail_if_count_ne: 2 + fail_if_not_matches_regexp: + - ".*8.8.8.8" + - ".*8.8.4.4" + + google_dot: # make sure google public dns returns the two expected AAAA records for dns.google over DoT + protocol: "dot" + server: "dns.google" + query_name: "dns.google" + query_type: "AAAA" + validate_answer_rrs: + fail_if_count_ne: 2 + fail_if_not_matches_regexp: + - ".*2001:4860:4860::8888" + - ".*2001:4860:4860::8844" + + cf_doh: # make sure cloudflare public dns returns a DNSSEC validated response for bornhack.dk over DoH + protocol: "doh" + server: "1dot1dot1dot1.cloudflare-dns.com" + query_name: "bornhack.dk" + query_type: "NS" + validate_response_flags: + fail_if_any_absent: + - "AD" + + has_ad: + edns_do: true + validate_response_flags: + fail_if_any_absent: + - "AD" + + has_no_ad: + edns_do: true + validate_response_flags: + fail_if_any_present: + - "AD" + + fail_recursive: + validate_response_flags: + fail_if_all_present: + - "QR" + - "RA" + - "RD" + + fail_not_auth: + validate_response_flags: + fail_if_all_absent: + - "AA" + - "AD" + + fail_auth_k_root: + validate_answer_rrs: + fail_if_matches_regexp: + - ".*k.root-servers.net" + + fail_additional_root: + validate_additional_rrs: + fail_if_all_match_regexp: + - "[a-m].root-servers.net.*" + + fail_answer_root: + validate_answer_rrs: + fail_if_not_matches_regexp: + - "[a-m].root-servers.net.*" + + fail_answer_root_none: + validate_answer_rrs: + fail_if_none_matches_regexp: + - ".*[a-m].root-servers.net.*" + +# socks1080: +# proxy: "socks5://127.0.0.1:1080" + +# https://dns-exporter.readthedocs.io/latest/examples.html +modules: + quad9_mx: + query_type: "MX" + server: "dns.quad9.net:53" + family: "ipv4" + gmail_mx_v4: + query_type: "MX" + query_name: "gmail.com" + family: "ipv4" + internalbind_a_v4: + query_type: "A" + family: "ipv4" + server: "127.0.0.1:53" diff --git a/roles/dns_exporter/test-requirements.txt b/roles/dns_exporter/test-requirements.txt new file mode 100644 index 000000000..7f0b6e759 --- /dev/null +++ b/roles/dns_exporter/test-requirements.txt @@ -0,0 +1 @@ +bcrypt diff --git a/roles/dns_exporter/vars/main.yml b/roles/dns_exporter/vars/main.yml new file mode 100644 index 000000000..ec6e74e1c --- /dev/null +++ b/roles/dns_exporter/vars/main.yml @@ -0,0 +1,9 @@ +--- +_dns_exporter_go_ansible_arch: "{{ {'i386': '386', + 'x86_64': 'amd64', + 'aarch64': 'arm64', + 'armv7l': 'armv7', + 'armv6l': 'armv6'}.get(ansible_architecture, ansible_architecture) }}" +_dns_exporter_repo: "tykling/dns_exporter" +_github_api_headers: "{{ {'GITHUB_TOKEN': lookup('ansible.builtin.env', 'GITHUB_TOKEN')} if (lookup('ansible.builtin.env', 'GITHUB_TOKEN')) else {} }}" +_dns_exporter_binaries: ['dns_exporter'] From 3a07f28702807d7b06a5d3b7808dd9ab9ca8a8f6 Mon Sep 17 00:00:00 2001 From: juju4 Date: Sun, 19 Jan 2025 00:17:03 +0000 Subject: [PATCH 2/6] ci: fix arguments-spec-lint Signed-off-by: juju4 --- roles/dns_exporter/meta/argument_specs.yml | 24 +++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/roles/dns_exporter/meta/argument_specs.yml b/roles/dns_exporter/meta/argument_specs.yml index 3e0df6d8c..c7cac27fb 100644 --- a/roles/dns_exporter/meta/argument_specs.yml +++ b/roles/dns_exporter/meta/argument_specs.yml @@ -8,6 +8,25 @@ argument_specs: author: - "Prometheus Community" options: + dns_exporter_web_listen_ip: + description: "IP address for dns_exporter to listen to" + default: "127.0.0.1" + dns_exporter_web_listen_port: + description: "Port for dns_exporter to listen to" + default: "15353" + dns_exporter_debug_enable: + description: "Enable or not daemon debug" + type: bool + default: false + dns_exporter_home: + description: "Home of dns_exporter user and base file locations" + default: "/var/lib/dns_exporter" + dns_exporter_virtualenv: + description: "Python virtualenv filepath for dns_exporter" + default: "/var/lib/dns_exporter/venv" + dns_exporter_config_template: + description: "Configuration template for dns_exporter" + default: "dns_exporter.yml.j2" dns_exporter_version: description: "DNS exporter package version. Also accepts latest as parameter." default: "1.0.0" @@ -61,11 +80,6 @@ argument_specs: dns_exporter_basic_auth_users: description: "Dictionary of users and password for basic authentication. Passwords are automatically hashed with bcrypt." type: "dict" - dns_exporter_binary_install_dir: - description: - - "I(Advanced)" - - "Directory to install dns_exporter binary" - default: "/usr/local/bin" dns_exporter_system_group: description: - "I(Advanced)" From 940c21e022203f5d6a86591fae99b5920807fb1d Mon Sep 17 00:00:00 2001 From: juju4 Date: Sun, 19 Jan 2025 00:20:08 +0000 Subject: [PATCH 3/6] ci: fix check-unused-variables Signed-off-by: juju4 --- roles/dns_exporter/defaults/main.yml | 6 ------ roles/dns_exporter/meta/argument_specs.yml | 16 ---------------- .../molecule/alternative/molecule.yml | 3 --- roles/dns_exporter/vars/main.yml | 1 - 4 files changed, 26 deletions(-) diff --git a/roles/dns_exporter/defaults/main.yml b/roles/dns_exporter/defaults/main.yml index fcca3769e..09f3f5c1e 100644 --- a/roles/dns_exporter/defaults/main.yml +++ b/roles/dns_exporter/defaults/main.yml @@ -1,8 +1,6 @@ --- dns_exporter_version: 1.0.0 # not go, but python pip. placeholders for _common -dns_exporter_binary_url: "" -dns_exporter_checksums_url: "" dns_exporter_textfile_dir: "" dns_exporter_enabled_collectors: [] dns_exporter_disabled_collectors: [] @@ -12,10 +10,8 @@ dns_exporter_basic_auth_users: {} # just for "prometheus.prometheus._common : Naive assertion of proper listen address" else not used dns_exporter_web_listen_address: "127.0.0.1:15353" -dns_exporter_web_disable_exporter_metrics: false dns_exporter_web_listen_ip: "127.0.0.1" dns_exporter_web_listen_port: "15353" -dns_exporter_web_telemetry_path: "/metrics" dns_exporter_debug_enable: false @@ -28,5 +24,3 @@ dns_exporter_system_group: "dns-exp" dns_exporter_system_user: "{{ dns_exporter_system_group }}" dns_exporter_config_dir: "/etc/dns_exporter" -# Local path to stash the archive and its extraction -dns_exporter_local_cache_path: "/tmp/dns_exporter-{{ ansible_system | lower }}-{{ _dns_exporter_go_ansible_arch }}/{{ dns_exporter_version }}" diff --git a/roles/dns_exporter/meta/argument_specs.yml b/roles/dns_exporter/meta/argument_specs.yml index c7cac27fb..1c4c0a6e3 100644 --- a/roles/dns_exporter/meta/argument_specs.yml +++ b/roles/dns_exporter/meta/argument_specs.yml @@ -30,22 +30,9 @@ argument_specs: dns_exporter_version: description: "DNS exporter package version. Also accepts latest as parameter." default: "1.0.0" - dns_exporter_binary_url: - description: "URL of the dns exporter binaries .tar.gz file" - default: "https://github.com/{{ _dns_exporter_repo }}/releases/download/v{{ dns_exporter_version }}/dns_exporter-{{ dns_exporter_version }}.{{ ansible_system | lower }}-{{ _dns_exporter_go_ansible_arch }}.tar.gz" - dns_exporter_checksums_url: - description: "URL of the dns exporter checksums file" - default: "https://github.com/{{ _dns_exporter_repo }}/releases/download/v{{ dns_exporter_version }}/sha256sums.txt" - dns_exporter_web_disable_exporter_metrics: - description: "Exclude metrics about the exporter itself (promhttp_*, process_*, go_*)." - type: bool - default: false dns_exporter_web_listen_address: description: "Address on which dns exporter will listen" default: "0.0.0.0:15353" - dns_exporter_web_telemetry_path: - description: "Path under which to expose metrics" - default: "/metrics" dns_exporter_enabled_collectors: description: - "List of dicts defining additionally enabled collectors and their configuration." @@ -90,9 +77,6 @@ argument_specs: - "I(Advanced)" - "DNS exporter user" default: "dns-exp" - dns_exporter_local_cache_path: - description: "Local path to stash the archive and its extraction" - default: "/tmp/dns_exporter-{{ ansible_system | lower }}-{{ _dns_exporter_go_ansible_arch }}/{{ dns_exporter_version }}" dns_exporter_config_dir: description: "Path to directory with dns_exporter configuration" default: "/etc/dns_exporter" diff --git a/roles/dns_exporter/molecule/alternative/molecule.yml b/roles/dns_exporter/molecule/alternative/molecule.yml index 333719833..9f03063e4 100644 --- a/roles/dns_exporter/molecule/alternative/molecule.yml +++ b/roles/dns_exporter/molecule/alternative/molecule.yml @@ -5,7 +5,6 @@ provisioner: inventory: group_vars: all: - dns_exporter_local_cache_path: "/tmp/dns_exporter-linux-amd64" dns_exporter_web_listen_address: - '127.0.0.1:8080' - '127.0.1.1:8080' @@ -22,5 +21,3 @@ provisioner: dns_exporter_basic_auth_users: randomuser: examplepassword dns_exporter_version: 1.5.0 - dns_exporter_binary_url: "https://github.com/prometheus/dns_exporter/releases/download/v{{\ - \ dns_exporter_version }}/dns_exporter-{{ dns_exporter_version }}.linux-amd64.tar.gz" diff --git a/roles/dns_exporter/vars/main.yml b/roles/dns_exporter/vars/main.yml index ec6e74e1c..a0ce2715c 100644 --- a/roles/dns_exporter/vars/main.yml +++ b/roles/dns_exporter/vars/main.yml @@ -6,4 +6,3 @@ _dns_exporter_go_ansible_arch: "{{ {'i386': '386', 'armv6l': 'armv6'}.get(ansible_architecture, ansible_architecture) }}" _dns_exporter_repo: "tykling/dns_exporter" _github_api_headers: "{{ {'GITHUB_TOKEN': lookup('ansible.builtin.env', 'GITHUB_TOKEN')} if (lookup('ansible.builtin.env', 'GITHUB_TOKEN')) else {} }}" -_dns_exporter_binaries: ['dns_exporter'] From 3cb0642bd51277f9eb9bad0cd6b4700058287845 Mon Sep 17 00:00:00 2001 From: juju4 Date: Sun, 19 Jan 2025 00:22:39 +0000 Subject: [PATCH 4/6] ci: fix check-unused-variables (2) Signed-off-by: juju4 --- roles/dns_exporter/vars/main.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/roles/dns_exporter/vars/main.yml b/roles/dns_exporter/vars/main.yml index a0ce2715c..bad473fa3 100644 --- a/roles/dns_exporter/vars/main.yml +++ b/roles/dns_exporter/vars/main.yml @@ -1,8 +1,3 @@ --- -_dns_exporter_go_ansible_arch: "{{ {'i386': '386', - 'x86_64': 'amd64', - 'aarch64': 'arm64', - 'armv7l': 'armv7', - 'armv6l': 'armv6'}.get(ansible_architecture, ansible_architecture) }}" _dns_exporter_repo: "tykling/dns_exporter" _github_api_headers: "{{ {'GITHUB_TOKEN': lookup('ansible.builtin.env', 'GITHUB_TOKEN')} if (lookup('ansible.builtin.env', 'GITHUB_TOKEN')) else {} }}" From 6d423ac07d9611d0a82e3bd65403f96ad903d41c Mon Sep 17 00:00:00 2001 From: juju4 Date: Sun, 19 Jan 2025 20:19:21 +0000 Subject: [PATCH 5/6] feat: add common dns providers as definitions/modules Signed-off-by: juju4 --- .../templates/dns_exporter.yml.j2 | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/roles/dns_exporter/templates/dns_exporter.yml.j2 b/roles/dns_exporter/templates/dns_exporter.yml.j2 index 17a590fef..a15db26c7 100644 --- a/roles/dns_exporter/templates/dns_exporter.yml.j2 +++ b/roles/dns_exporter/templates/dns_exporter.yml.j2 @@ -235,10 +235,42 @@ modules: query_type: "MX" server: "dns.quad9.net:53" family: "ipv4" + quad9_a_v4: + query_type: "A" + server: "dns.quad9.net:53" + family: "ipv4" + quad9_aaaa_v6: + query_type: "AAAA" + server: "dns.quad9.net:53" + family: "ipv6" gmail_mx_v4: query_type: "MX" query_name: "gmail.com" family: "ipv4" + google_a_v4: + query_type: "A" + server: "dns.google:53" + family: "ipv4" + google_aaaa_v6: + query_type: "AAAA" + server: "dns.google:53" + family: "ipv6" + cloudflare_a_v4: + query_type: "A" + server: "one.one.one.one:53" + family: "ipv4" + cloudflare_aaaa_v6: + query_type: "AAAA" + server: "one.one.one.one:53" + family: "ipv6" + opendns_a_v4: + query_type: "A" + server: "208.67.222.222:53" + family: "ipv4" + opendns_aaaa_v6: + query_type: "AAAA" + server: "208.67.222.222:53" + family: "ipv6" internalbind_a_v4: query_type: "A" family: "ipv4" From 4f48f9b160131591665247ed7547dabd1da997b7 Mon Sep 17 00:00:00 2001 From: juju4 Date: Sun, 19 Jan 2025 20:19:57 +0000 Subject: [PATCH 6/6] ci: add test/integration/targets Signed-off-by: juju4 --- .../targets/molecule-dns_exporter-alternative/runme.sh | 4 ++++ .../targets/molecule-dns_exporter-default/runme.sh | 4 ++++ .../integration/targets/molecule-dns_exporter-latest/runme.sh | 4 ++++ 3 files changed, 12 insertions(+) create mode 100755 tests/integration/targets/molecule-dns_exporter-alternative/runme.sh create mode 100755 tests/integration/targets/molecule-dns_exporter-default/runme.sh create mode 100755 tests/integration/targets/molecule-dns_exporter-latest/runme.sh diff --git a/tests/integration/targets/molecule-dns_exporter-alternative/runme.sh b/tests/integration/targets/molecule-dns_exporter-alternative/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-dns_exporter-alternative/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +collection_root=$(pwd | grep -oP ".+\/ansible_collections\/\w+?\/\w+") +source "$collection_root/tests/integration/molecule.sh" diff --git a/tests/integration/targets/molecule-dns_exporter-default/runme.sh b/tests/integration/targets/molecule-dns_exporter-default/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-dns_exporter-default/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +collection_root=$(pwd | grep -oP ".+\/ansible_collections\/\w+?\/\w+") +source "$collection_root/tests/integration/molecule.sh" diff --git a/tests/integration/targets/molecule-dns_exporter-latest/runme.sh b/tests/integration/targets/molecule-dns_exporter-latest/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-dns_exporter-latest/runme.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +collection_root=$(pwd | grep -oP ".+\/ansible_collections\/\w+?\/\w+") +source "$collection_root/tests/integration/molecule.sh"