diff --git a/roles/influxdb_exporter/README.md b/roles/influxdb_exporter/README.md new file mode 100644 index 000000000..ed3df3b74 --- /dev/null +++ b/roles/influxdb_exporter/README.md @@ -0,0 +1,54 @@ +# Ansible Role: influxdb exporter + +## Description + +Deploy prometheus [Influxdb exporter](https://github.com/prometheus/influxdb_exporter) using ansible. + +## 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`) + +## 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/influxdb_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.influxdb_exporter +``` + +### Demo site + +We provide an example site that demonstrates a full monitoring solution based on prometheus and grafana. +The repository with code and links to running instances is [available on github](https://github.com/prometheus/demo-site) and the site is hosted on [DigitalOcean](https://digitalocean.com). + +## 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). + +## License + +This project is licensed under MIT License. See [LICENSE](/LICENSE) for more details. diff --git a/roles/influxdb_exporter/defaults/main.yml b/roles/influxdb_exporter/defaults/main.yml new file mode 100644 index 000000000..394fcf84b --- /dev/null +++ b/roles/influxdb_exporter/defaults/main.yml @@ -0,0 +1,22 @@ +--- +influxdb_exporter_version: "0.11.5" +influxdb_exporter_binary_url: "https://github.com/{{ _influxdb_exporter_repo }}/releases/download/v{{ influxdb_exporter_version }}/\ + influxdb_exporter-{{ influxdb_exporter_version }}.{{ ansible_system | lower }}-{{ _influxdb_exporter_go_ansible_arch }}.tar.gz" +influxdb_exporter_checksums_url: "https://github.com/{{ _influxdb_exporter_repo }}/releases/download/v{{ influxdb_exporter_version }}/sha256sums.txt" + +influxdb_exporter_web_listen_address: "0.0.0.0:9122" +influxdb_exporter_udp_bind_address: "{{ influxdb_exporter_web_listen_address }}" +influxdb_exporter_web_telemetry_path: "/metrics" +influxdb_exporter_exporter_web_telemetry_path: "/metrics/exporter" +influxdb_exporter_influxdb_sample_expiry: "5m" +influxdb_exporter_log_level: "info" +influxdb_exporter_log_format: "logfmt" +influxdb_exporter_export_timestamps: true + +influxdb_exporter_binary_install_dir: "/usr/local/bin" +influxdb_exporter_system_group: "influxdb-exp" +influxdb_exporter_system_user: "{{ influxdb_exporter_system_group }}" + +influxdb_exporter_config_dir: "/etc/influxdb_exporter" +# Local path to stash the archive and its extraction +influxdb_exporter_local_cache_path: "/tmp/influxdb_exporter-{{ ansible_system | lower }}-{{ _influxdb_exporter_go_ansible_arch }}/{{ influxdb_exporter_version }}" diff --git a/roles/influxdb_exporter/handlers/main.yml b/roles/influxdb_exporter/handlers/main.yml new file mode 100644 index 000000000..ccdfd1c12 --- /dev/null +++ b/roles/influxdb_exporter/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: Restart influxdb_exporter + listen: "restart influxdb_exporter" + become: true + ansible.builtin.systemd: + daemon_reload: true + name: influxdb_exporter + state: restarted + when: + - not ansible_check_mode diff --git a/roles/influxdb_exporter/meta/argument_specs.yml b/roles/influxdb_exporter/meta/argument_specs.yml new file mode 100644 index 000000000..143fe2307 --- /dev/null +++ b/roles/influxdb_exporter/meta/argument_specs.yml @@ -0,0 +1,66 @@ +--- +# yamllint disable rule:line-length +argument_specs: + main: + short_description: "Prometheus Influxdb Exporter" + description: + - "Deploy prometheus L(influxdb exporter,https://github.com/prometheus/influxdb_exporter) using ansible" + author: + - "Prometheus Community" + options: + influxdb_exporter_version: + description: "influxdb exporter package version. Also accepts latest as parameter." + default: "0.11.5" + influxdb_exporter_binary_url: + description: "URL of the influxdb exporter binaries .tar.gz file" + default: "https://github.com/{{ _influxdb_exporter_repo }}/releases/download/v{{ influxdb_exporter_version }}/influxdb_exporter-{{ influxdb_exporter_version }}.{{ ansible_system | lower }}-{{ _influxdb_exporter_go_ansible_arch }}.tar.gz" + influxdb_exporter_checksums_url: + description: "URL of the influxdb exporter checksums file" + default: "https://github.com/{{ _influxdb_exporter_repo }}/releases/download/v{{ influxdb_exporter_version }}/sha256sums.txt" + influxdb_exporter_web_listen_address: + description: "Address on which influxdb exporter will listen" + default: "0.0.0.0:9122" + influxdb_exporter_web_telemetry_path: + description: "Path under which to expose metrics" + default: "/metrics" + influxdb_exporter_influxdb_sample_expiry: + description: "How long a sample is valid for" + default: "5m" + influxdb_exporter_udp_bind_address: + description: "Address on which to listen for udp packets" + default: "0.0.0.0:9122" + influxdb_exporter_exporter_web_telemetry_path: + description: "Path under which to expose metrics" + default: "/metrics" + influxdb_exporter_log_level: + default: "info" + description: "Only log messages with the given severity or above" + choices: ["debug", "info", "warn", "error"] + influxdb_exporter_log_format: + default: "logfmt" + description: "Output format of log messages" + choices: ["logfmt", "json"] + influxdb_exporter_export_timestamps: + default: true + description: "Export timestamps of points" + influxdb_exporter_binary_install_dir: + description: + - "I(Advanced)" + - "Directory to install influxdb_exporter binary" + default: "/usr/local/bin" + influxdb_exporter_system_group: + description: + - "I(Advanced)" + - "System group for influxdb exporter" + default: "influxdb-exp" + influxdb_exporter_system_user: + description: + - "I(Advanced)" + - "influxdb exporter user" + default: "influxdb-exp" + influxdb_exporter_local_cache_path: + description: 'Local path to stash the archive and its extraction' + default: "/tmp/influxdb_exporter-{{ ansible_system | lower }}-{{ _influxdb_exporter_go_ansible_arch }}/{{ influxdb_exporter_version }}" + influxdb_exporter_config_dir: + description: "Path to directory with influxdb_exporter configuration" + default: "/etc/influxdb_exporter" diff --git a/roles/influxdb_exporter/meta/main.yml b/roles/influxdb_exporter/meta/main.yml new file mode 100644 index 000000000..d1ba9cbac --- /dev/null +++ b/roles/influxdb_exporter/meta/main.yml @@ -0,0 +1,25 @@ +--- +galaxy_info: + author: "Prometheus Community" + description: "Prometheus Influxdb 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/influxdb_exporter/molecule/alternative/molecule.yml b/roles/influxdb_exporter/molecule/alternative/molecule.yml new file mode 100644 index 000000000..890224131 --- /dev/null +++ b/roles/influxdb_exporter/molecule/alternative/molecule.yml @@ -0,0 +1,10 @@ +--- +provisioner: + playbooks: + prepare: "${MOLECULE_PROJECT_DIRECTORY}/../../.config/molecule/alternative/prepare.yml" + inventory: + group_vars: + all: + influxdb_exporter_web_listen_address: '127.0.0.1:8080' + + influxdb_exporter_version: 0.11.2 diff --git a/roles/influxdb_exporter/molecule/alternative/tests/test_alternative.py b/roles/influxdb_exporter/molecule/alternative/tests/test_alternative.py new file mode 100644 index 000000000..6e2a73bc9 --- /dev/null +++ b/roles/influxdb_exporter/molecule/alternative/tests/test_alternative.py @@ -0,0 +1,27 @@ +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_service(host): + s = host.service("influxdb_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u influxdb_exporter --since "1 hour ago"') + print("\n==== journalctl -u influxdb_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +@pytest.mark.parametrize("sockets", [ + "tcp://127.0.0.1:8080", +]) +def test_socket(host, sockets): + assert host.socket(sockets).is_listening diff --git a/roles/influxdb_exporter/molecule/default/molecule.yml b/roles/influxdb_exporter/molecule/default/molecule.yml new file mode 100644 index 000000000..3da70d0c9 --- /dev/null +++ b/roles/influxdb_exporter/molecule/default/molecule.yml @@ -0,0 +1,6 @@ +--- +provisioner: + inventory: + group_vars: + all: + influxdb_exporter_web_listen_address: "127.0.0.1:9122" diff --git a/roles/influxdb_exporter/molecule/default/tests/test_default.py b/roles/influxdb_exporter/molecule/default/tests/test_default.py new file mode 100644 index 000000000..84cf40ab1 --- /dev/null +++ b/roles/influxdb_exporter/molecule/default/tests/test_default.py @@ -0,0 +1,61 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from testinfra_helpers import get_target_hosts + +testinfra_hosts = get_target_hosts() + + +def test_files(host): + files = [ + "/etc/systemd/system/influxdb_exporter.service", + "/usr/local/bin/influxdb_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("influxdb-exp").exists + assert "influxdb-exp" in host.user("influxdb-exp").groups + assert host.user("influxdb-exp").shell == "/usr/sbin/nologin" + assert host.user("influxdb-exp").home == "/etc/influxdb_exporter" + + +def test_service(host): + s = host.service("influxdb_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u influxdb_exporter --since "1 hour ago"') + print("\n==== journalctl -u influxdb_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_socket(host): + sockets = [ + "tcp://127.0.0.1:9122" + ] + for socket in sockets: + s = host.socket(socket) + assert s.is_listening diff --git a/roles/influxdb_exporter/molecule/latest/molecule.yml b/roles/influxdb_exporter/molecule/latest/molecule.yml new file mode 100644 index 000000000..228dfa3ad --- /dev/null +++ b/roles/influxdb_exporter/molecule/latest/molecule.yml @@ -0,0 +1,6 @@ +--- +provisioner: + inventory: + group_vars: + all: + influxdb_exporter_version: latest diff --git a/roles/influxdb_exporter/molecule/latest/tests/test_latest.py b/roles/influxdb_exporter/molecule/latest/tests/test_latest.py new file mode 100644 index 000000000..0107d9cdb --- /dev/null +++ b/roles/influxdb_exporter/molecule/latest/tests/test_latest.py @@ -0,0 +1,35 @@ +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/influxdb_exporter.service", + "/usr/local/bin/influxdb_exporter" +]) +def test_files(host, files): + f = host.file(files) + assert f.exists + assert f.is_file + + +def test_service(host): + s = host.service("influxdb_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u influxdb_exporter --since "1 hour ago"') + print("\n==== journalctl -u influxdb_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_socket(host): + s = host.socket("tcp://0.0.0.0:9122") + assert s.is_listening diff --git a/roles/influxdb_exporter/tasks/main.yml b/roles/influxdb_exporter/tasks/main.yml new file mode 100644 index 000000000..e244bff0b --- /dev/null +++ b/roles/influxdb_exporter/tasks/main.yml @@ -0,0 +1,69 @@ +--- +- name: Preflight + ansible.builtin.include_tasks: + file: preflight.yml + tags: + - influxdb_exporter + - install + - configure + - run + - influxdb_exporter_install + - influxdb_exporter_configure + - influxdb_exporter_run + +- name: Install + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: install.yml + vars: + _common_local_cache_path: "{{ influxdb_exporter_local_cache_path }}" + _common_binaries: "{{ _influxdb_exporter_binaries }}" + _common_binary_install_dir: "{{ influxdb_exporter_binary_install_dir }}" + _common_binary_url: "{{ influxdb_exporter_binary_url }}" + _common_checksums_url: "{{ influxdb_exporter_checksums_url }}" + _common_system_group: "{{ influxdb_exporter_system_group }}" + _common_system_user: "{{ influxdb_exporter_system_user }}" + _common_config_dir: "{{ influxdb_exporter_config_dir }}" + _common_binary_unarchive_opts: ['--strip-components=1'] + tags: + - influxdb_exporter + - install + - influxdb_exporter_install + +- name: SELinux + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: selinux.yml + vars: + _common_selinux_port: "{{ influxdb_exporter_web_listen_address | urlsplit('port') }}" + when: ansible_selinux.status == "enabled" + tags: + - influxdb_exporter + - configure + - influxdb_exporter_configure + +- name: Configure + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: configure.yml + vars: + _common_system_user: "{{ influxdb_exporter_system_user }}" + _common_system_group: "{{ influxdb_exporter_system_group }}" + tags: + - influxdb_exporter + - configure + - influxdb_exporter_configure + +- name: Ensure Influxdb Exporter is enabled on boot + become: true + ansible.builtin.systemd: + daemon_reload: true + name: influxdb_exporter + enabled: true + state: started + when: + - not ansible_check_mode + tags: + - influxdb_exporter + - run + - influxdb_exporter_run diff --git a/roles/influxdb_exporter/tasks/preflight.yml b/roles/influxdb_exporter/tasks/preflight.yml new file mode 100644 index 000000000..938f1d55c --- /dev/null +++ b/roles/influxdb_exporter/tasks/preflight.yml @@ -0,0 +1,48 @@ +--- +- name: Common preflight + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: preflight.yml + tags: + - influxdb_exporter + - preflight + - influxdb_exporter_preflight + +- name: Assert usage of systemd as an init system + ansible.builtin.assert: + that: ansible_service_mgr == 'systemd' + msg: "This role only works with systemd" + tags: + - influxdb_exporter + - preflight + - influxdb_exporter_preflight + +- name: Naive assertion of proper listen address + ansible.builtin.assert: + that: + - >- + [influxdb_exporter_web_listen_address] | + flatten | + reject('match', '.+:\\d+$') | + list | + length == 0 + tags: + - influxdb_exporter + - preflight + - influxdb_exporter_preflight + +- name: Discover latest version + ansible.builtin.set_fact: + influxdb_exporter_version: "{{ (lookup('url', 'https://api.github.com/repos/{{ _influxdb_exporter_repo }}/releases/latest', headers=_influxdb_exporter_github_api_headers, + split_lines=False) | from_json).get('tag_name') | replace('v', '') }}" + run_once: true + until: influxdb_exporter_version is version('0.0.0', '>=') + retries: 10 + when: + - influxdb_exporter_version == "latest" + tags: + - influxdb_exporter + - install + - influxdb_exporter_install + - download + - influxdb_exporter_download diff --git a/roles/influxdb_exporter/templates/influxdb_exporter.service.j2 b/roles/influxdb_exporter/templates/influxdb_exporter.service.j2 new file mode 100644 index 000000000..ec47a40e1 --- /dev/null +++ b/roles/influxdb_exporter/templates/influxdb_exporter.service.j2 @@ -0,0 +1,31 @@ +{{ ansible_managed | comment }} + +[Unit] +Description=Influxdb to Prometheus Exporter Service +After=network-online.target + +[Service] +Type=simple +User={{ influxdb_exporter_system_user }} +Group={{ influxdb_exporter_system_group }} +ExecStart={{ influxdb_exporter_binary_install_dir }}/influxdb_exporter \ + '--web.listen-address={{ influxdb_exporter_web_listen_address }}' \ + '--udp.bind-address={{ influxdb_exporter_udp_bind_address }}' \ + '--web.telemetry-path={{ influxdb_exporter_web_telemetry_path }}' \ + '--web.exporter-telemetry-path={{ influxdb_exporter_exporter_web_telemetry_path }}' \ + '--influxdb.sample-expiry={{ influxdb_exporter_influxdb_sample_expiry }}' \ + '--log.level={{ influxdb_exporter_log_level }}' \ + '--log.format={{ influxdb_exporter_log_format }}' \ +{% if influxdb_exporter_export_timestamps %} + '--timestamps' +{% else %} + '--no-timestamps' +{% endif %} + +SyslogIdentifier=influxdb_exporter +Restart=always +RestartSec=1 +StartLimitInterval=0 + +[Install] +WantedBy=multi-user.target diff --git a/roles/influxdb_exporter/test-requirements.txt b/roles/influxdb_exporter/test-requirements.txt new file mode 100644 index 000000000..7f0b6e759 --- /dev/null +++ b/roles/influxdb_exporter/test-requirements.txt @@ -0,0 +1 @@ +bcrypt diff --git a/roles/influxdb_exporter/vars/main.yml b/roles/influxdb_exporter/vars/main.yml new file mode 100644 index 000000000..ad5a957a3 --- /dev/null +++ b/roles/influxdb_exporter/vars/main.yml @@ -0,0 +1,10 @@ +--- +_influxdb_exporter_go_ansible_arch: "{{ {'i386': '386', + 'x86_64': 'amd64', + 'aarch64': 'arm64', + 'armv7l': 'armv7', + 'armv6l': 'armv6'}.get(ansible_architecture, ansible_architecture) }}" +_influxdb_exporter_repo: "prometheus/influxdb_exporter" +_influxdb_exporter_github_api_headers: "{{ {'GITHUB_TOKEN': lookup('ansible.builtin.env', 'GITHUB_TOKEN')} if (lookup('ansible.builtin.env', 'GITHUB_TOKEN')) else {} }}" + +_influxdb_exporter_binaries: ['influxdb_exporter'] diff --git a/tests/integration/targets/molecule-influxdb_exporter-alternative/runme.sh b/tests/integration/targets/molecule-influxdb_exporter-alternative/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-influxdb_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-influxdb_exporter-default/runme.sh b/tests/integration/targets/molecule-influxdb_exporter-default/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-influxdb_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-influxdb_exporter-latest/runme.sh b/tests/integration/targets/molecule-influxdb_exporter-latest/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-influxdb_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"