diff --git a/roles/elasticsearch_exporter/README.md b/roles/elasticsearch_exporter/README.md new file mode 100644 index 000000000..695d31261 --- /dev/null +++ b/roles/elasticsearch_exporter/README.md @@ -0,0 +1,45 @@ +# Ansible Role: elasticsearch exporter + +## Description + +Deploy prometheus [elasticsearch exporter](https://github.com/prometheus-community/elasticsearch_exporter) using ansible. + +Grafana dashboard example: https://github.com/prometheus-community/elasticsearch_exporter/blob/master/examples/grafana/dashboard.json + +## 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/elasticsearch_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.elasticsearch_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`. + +## 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/elasticsearch_exporter/TROUBLESHOOTING.md b/roles/elasticsearch_exporter/TROUBLESHOOTING.md new file mode 100644 index 000000000..4f341277d --- /dev/null +++ b/roles/elasticsearch_exporter/TROUBLESHOOTING.md @@ -0,0 +1 @@ +# Troubleshooting diff --git a/roles/elasticsearch_exporter/defaults/main.yml b/roles/elasticsearch_exporter/defaults/main.yml new file mode 100644 index 000000000..dd25deeaa --- /dev/null +++ b/roles/elasticsearch_exporter/defaults/main.yml @@ -0,0 +1,37 @@ +--- +elasticsearch_exporter_version: 1.8.0 +elasticsearch_exporter_binary_url: "https://github.com/{{ _elasticsearch_exporter_repo }}/releases/download/v{{ elasticsearch_exporter_version }}/\ + elasticsearch_exporter-{{ elasticsearch_exporter_version }}.{{ ansible_facts['system'] | lower }}-{{ _elasticsearch_exporter_go_ansible_arch }}.tar.gz" +elasticsearch_exporter_checksums_url: "https://github.com/{{ _elasticsearch_exporter_repo }}/releases/download/v{{ elasticsearch_exporter_version }}/sha256sums.txt" + +elasticsearch_exporter_web_disable_exporter_metrics: false +elasticsearch_exporter_web_listen_address: "0.0.0.0:9114" +elasticsearch_exporter_web_telemetry_path: "/metrics" + +elasticsearch_exporter_textfile_dir: "/var/lib/elasticsearch_exporter" + +elasticsearch_exporter_tls_server_config: {} + +elasticsearch_exporter_http_server_config: {} + +elasticsearch_exporter_basic_auth_users: {} + +elasticsearch_exporter_enabled_collectors: + - systemd + - textfile: + directory: "{{ elasticsearch_exporter_textfile_dir }}" +# - filesystem: +# ignored-mount-points: "^/(sys|proc|dev)($|/)" +# ignored-fs-types: "^(sys|proc|auto)fs$" + +elasticsearch_exporter_disabled_collectors: [] + +elasticsearch_exporter_binary_install_dir: "/usr/local/bin" +elasticsearch_exporter_system_group: "elastic-exp" +elasticsearch_exporter_system_user: "{{ elasticsearch_exporter_system_group }}" +# systemd cli extra arguments +elasticsearch_exporter_extra_args: [] + +elasticsearch_exporter_config_dir: "/etc/elasticsearch_exporter" +# Local path to stash the archive and its extraction +elasticsearch_exporter_local_cache_path: "/tmp/elasticsearch_exporter-{{ ansible_facts['system'] | lower }}-{{ _elasticsearch_exporter_go_ansible_arch }}/{{ elasticsearch_exporter_version }}" diff --git a/roles/elasticsearch_exporter/handlers/main.yml b/roles/elasticsearch_exporter/handlers/main.yml new file mode 100644 index 000000000..7c16b8bb4 --- /dev/null +++ b/roles/elasticsearch_exporter/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: Restart elasticsearch_exporter + listen: "restart elasticsearch_exporter" + become: true + ansible.builtin.systemd: + daemon_reload: true + name: elasticsearch_exporter + state: restarted + when: + - not ansible_check_mode diff --git a/roles/elasticsearch_exporter/meta/argument_specs.yml b/roles/elasticsearch_exporter/meta/argument_specs.yml new file mode 100644 index 000000000..49335bfb9 --- /dev/null +++ b/roles/elasticsearch_exporter/meta/argument_specs.yml @@ -0,0 +1,89 @@ +--- +# yamllint disable rule:line-length +argument_specs: + main: + short_description: "Prometheus Elasticsearch Exporter" + description: + - "Deploy prometheus L(elasticsearch exporter,https://github.com/prometheus-community/elasticsearch_exporter) using ansible" + author: + - "Prometheus Community" + options: + elasticsearch_exporter_version: + description: "Elasticsearch exporter package version. Also accepts latest as parameter." + default: "1.8.0" + elasticsearch_exporter_binary_url: + description: "URL of the elasticsearch exporter binaries .tar.gz file" + default: "https://github.com/{{ _elasticsearch_exporter_repo }}/releases/download/v{{ elasticsearch_exporter_version }}/elasticsearch_exporter-{{ elasticsearch_exporter_version }}.{{ ansible_facts['system'] | lower }}-{{ _elasticsearch_exporter_go_ansible_arch }}.tar.gz" + elasticsearch_exporter_checksums_url: + description: "URL of the elasticsearch exporter checksums file" + default: "https://github.com/{{ _elasticsearch_exporter_repo }}/releases/download/v{{ elasticsearch_exporter_version }}/sha256sums.txt" + elasticsearch_exporter_web_disable_exporter_metrics: + description: "Exclude metrics about the exporter itself (promhttp_*, process_*, go_*)." + type: bool + default: false + elasticsearch_exporter_web_listen_address: + description: "Address on which elasticsearch exporter will listen" + default: "0.0.0.0:9114" + elasticsearch_exporter_web_telemetry_path: + description: "Path under which to expose metrics" + default: "/metrics" + elasticsearch_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/elasticsearch_exporter#enabled-by-default)." + type: "list" + default: + - systemd + - textfile: + directory: "{{ elasticsearch_exporter_textfile_dir }}" + elasticsearch_exporter_disabled_collectors: + description: + - "List of disabled collectors." + - "By default elasticsearch_exporter disables collectors listed L(here,https://github.com/prometheus/elasticsearch_exporter#disabled-by-default)." + type: "list" + elements: "str" + elasticsearch_exporter_textfile_dir: + description: + - "Directory used by the L(Textfile Collector,https://github.com/prometheus/elasticsearch_exporter#textfile-collector)." + - "To get permissions to write metrics in this directory, users must be in C(elastic-exp) system group." + - "B(Note:) More information in TROUBLESHOOTING.md guide." + default: "/var/lib/elasticsearch_exporter" + elasticsearch_exporter_tls_server_config: + description: + - "Configuration for TLS authentication." + - "Keys and values are the same as in L(elasticsearch_exporter docs,https://prometheus.io/docs/prometheus/latest/configuration/https/)." + type: "dict" + elasticsearch_exporter_http_server_config: + description: + - "Config for HTTP/2 support." + - "Keys and values are the same as in L(elasticsearch_exporter docs,https://prometheus.io/docs/prometheus/latest/configuration/https/)." + type: "dict" + elasticsearch_exporter_basic_auth_users: + description: "Dictionary of users and password for basic authentication. Passwords are automatically hashed with bcrypt." + type: "dict" + elasticsearch_exporter_binary_install_dir: + description: + - "I(Advanced)" + - "Directory to install elasticsearch_exporter binary" + default: "/usr/local/bin" + elasticsearch_exporter_system_group: + description: + - "I(Advanced)" + - "System group for elasticsearch exporter" + default: "elastic-exp" + elasticsearch_exporter_system_user: + description: + - "I(Advanced)" + - "Elasticsearch exporter user" + default: "elastic-exp" + elasticsearch_exporter_extra_args: + description: + - "I(Advanced)" + - "Elasticsearch exporter cli extra arguments to be added to systemd unit" + default: [] + elasticsearch_exporter_local_cache_path: + description: "Local path to stash the archive and its extraction" + default: "/tmp/elasticsearch_exporter-{{ ansible_facts['system'] | lower }}-{{ _elasticsearch_exporter_go_ansible_arch }}/{{ elasticsearch_exporter_version }}" + elasticsearch_exporter_config_dir: + description: "Path to directory with elasticsearch_exporter configuration" + default: "/etc/elasticsearch_exporter" diff --git a/roles/elasticsearch_exporter/meta/main.yml b/roles/elasticsearch_exporter/meta/main.yml new file mode 100644 index 000000000..3512fdd96 --- /dev/null +++ b/roles/elasticsearch_exporter/meta/main.yml @@ -0,0 +1,25 @@ +--- +galaxy_info: + author: "Prometheus Community" + description: "Prometheus Elasticsearch 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/elasticsearch_exporter/molecule/alternative/molecule.yml b/roles/elasticsearch_exporter/molecule/alternative/molecule.yml new file mode 100644 index 000000000..a09959be0 --- /dev/null +++ b/roles/elasticsearch_exporter/molecule/alternative/molecule.yml @@ -0,0 +1,26 @@ +--- +provisioner: + playbooks: + prepare: "${MOLECULE_PROJECT_DIRECTORY}/../../.config/molecule/alternative/prepare.yml" + inventory: + group_vars: + all: + elasticsearch_exporter_local_cache_path: "/tmp/elasticsearch_exporter-linux-amd64" + elasticsearch_exporter_web_listen_address: + - '127.0.0.1:8080' + - '127.0.1.1:8080' + elasticsearch_exporter_textfile_dir: "" + elasticsearch_exporter_enabled_collectors: + - entropy + elasticsearch_exporter_disabled_collectors: + - diskstats + elasticsearch_exporter_tls_server_config: + cert_file: /etc/elasticsearch_exporter/tls.cert + key_file: /etc/elasticsearch_exporter/tls.key + elasticsearch_exporter_http_server_config: + http2: true + elasticsearch_exporter_basic_auth_users: + randomuser: examplepassword + elasticsearch_exporter_version: 1.8.0 + elasticsearch_exporter_binary_url: "https://github.com/prometheus-community/elasticsearch_exporter/releases/download/v{{\ + \ elasticsearch_exporter_version }}/elasticsearch_exporter-{{ elasticsearch_exporter_version }}.linux-amd64.tar.gz" diff --git a/roles/elasticsearch_exporter/molecule/alternative/tests/test_alternative.py b/roles/elasticsearch_exporter/molecule/alternative/tests/test_alternative.py new file mode 100644 index 000000000..7121f78b6 --- /dev/null +++ b/roles/elasticsearch_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/elasticsearch_exporter" + ] + for dir in dirs: + d = host.file(dir) + assert not d.exists + + +def test_service(host): + s = host.service("elasticsearch_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u elasticsearch_exporter --since "1 hour ago"') + print("\n==== journalctl -u elasticsearch_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_protecthome_property(host): + s = host.service("elasticsearch_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/elasticsearch_exporter/molecule/default/molecule.yml b/roles/elasticsearch_exporter/molecule/default/molecule.yml new file mode 100644 index 000000000..97776d920 --- /dev/null +++ b/roles/elasticsearch_exporter/molecule/default/molecule.yml @@ -0,0 +1,6 @@ +--- +provisioner: + inventory: + group_vars: + all: + elasticsearch_exporter_web_listen_address: "127.0.0.1:9114" diff --git a/roles/elasticsearch_exporter/molecule/default/tests/test_default.py b/roles/elasticsearch_exporter/molecule/default/tests/test_default.py new file mode 100644 index 000000000..414559082 --- /dev/null +++ b/roles/elasticsearch_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/elasticsearch_exporter" + ] + for dir in dirs: + d = host.file(dir) + assert d.is_directory + assert d.exists + + +def test_files(host): + files = [ + "/etc/systemd/system/elasticsearch_exporter.service", + "/usr/local/bin/elasticsearch_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("elastic-exp").exists + assert "elastic-exp" in host.user("elastic-exp").groups + assert host.user("elastic-exp").shell == "/usr/sbin/nologin" + + +def test_service(host): + s = host.service("elasticsearch_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u elasticsearch_exporter --since "1 hour ago"') + print("\n==== journalctl -u elasticsearch_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_protecthome_property(host): + s = host.service("elasticsearch_exporter") + p = s.systemd_properties + assert p.get("ProtectHome") == "yes" + + +def test_socket(host): + sockets = [ + "tcp://127.0.0.1:9114" + ] + for socket in sockets: + s = host.socket(socket) + assert s.is_listening diff --git a/roles/elasticsearch_exporter/molecule/latest/molecule.yml b/roles/elasticsearch_exporter/molecule/latest/molecule.yml new file mode 100644 index 000000000..9fec08ac9 --- /dev/null +++ b/roles/elasticsearch_exporter/molecule/latest/molecule.yml @@ -0,0 +1,7 @@ +--- +provisioner: + inventory: + group_vars: + all: + elasticsearch_exporter_version: latest + elasticsearch_exporter_textfile_dir: /home/elasticsearch_exporter diff --git a/roles/elasticsearch_exporter/molecule/latest/tests/test_latest.py b/roles/elasticsearch_exporter/molecule/latest/tests/test_latest.py new file mode 100644 index 000000000..64c261087 --- /dev/null +++ b/roles/elasticsearch_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/elasticsearch_exporter.service", + "/usr/local/bin/elasticsearch_exporter" +]) +def test_files(host, files): + f = host.file(files) + assert f.exists + assert f.is_file + + +def test_directories(host): + dirs = [ + "/home/elasticsearch_exporter" + ] + for dir in dirs: + d = host.file(dir) + assert d.is_directory + assert d.exists + + +def test_service(host): + s = host.service("elasticsearch_exporter") + try: + assert s.is_running + except AssertionError: + # Capture service logs + journal_output = host.run('journalctl -u elasticsearch_exporter --since "1 hour ago"') + print("\n==== journalctl -u elasticsearch_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise # Re-raise the original assertion error + + +def test_protecthome_property(host): + s = host.service("elasticsearch_exporter") + p = s.systemd_properties + assert p.get("ProtectHome") == "read-only" + + +def test_socket(host): + s = host.socket("tcp://0.0.0.0:9114") + assert s.is_listening diff --git a/roles/elasticsearch_exporter/tasks/configure.yml b/roles/elasticsearch_exporter/tasks/configure.yml new file mode 100644 index 000000000..1f2a12581 --- /dev/null +++ b/roles/elasticsearch_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: "{{ elasticsearch_exporter_system_user }}" + _common_system_group: "{{ elasticsearch_exporter_system_group }}" + _common_config_dir: "{{ elasticsearch_exporter_config_dir }}" + _common_tls_server_config: "{{ elasticsearch_exporter_tls_server_config }}" + _common_http_server_config: "{{ elasticsearch_exporter_http_server_config }}" + _common_basic_auth_users: "{{ elasticsearch_exporter_basic_auth_users }}" + tags: + - elasticsearch_exporter + - configure + - elasticsearch_exporter_configure + +- name: Create textfile collector dir + ansible.builtin.file: + path: "{{ elasticsearch_exporter_textfile_dir }}" + state: directory + owner: "{{ elasticsearch_exporter_system_user }}" + group: "{{ elasticsearch_exporter_system_group }}" + mode: u+rwX,g+rwX,o=rX + become: true + when: elasticsearch_exporter_textfile_dir | length > 0 + tags: + - elasticsearch_exporter + - configure + - elasticsearch_exporter_configure diff --git a/roles/elasticsearch_exporter/tasks/main.yml b/roles/elasticsearch_exporter/tasks/main.yml new file mode 100644 index 000000000..b11349d96 --- /dev/null +++ b/roles/elasticsearch_exporter/tasks/main.yml @@ -0,0 +1,53 @@ +--- +- name: Preflight + ansible.builtin.include_tasks: + file: preflight.yml + tags: + - elasticsearch_exporter_install + - elasticsearch_exporter_configure + - elasticsearch_exporter_run + +- name: Install + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: install.yml + vars: + _common_local_cache_path: "{{ elasticsearch_exporter_local_cache_path }}" + _common_binaries: "{{ _elasticsearch_exporter_binaries }}" + _common_binary_install_dir: "{{ elasticsearch_exporter_binary_install_dir }}" + _common_binary_url: "{{ elasticsearch_exporter_binary_url }}" + _common_checksums_url: "{{ elasticsearch_exporter_checksums_url }}" + _common_system_group: "{{ elasticsearch_exporter_system_group }}" + _common_system_user: "{{ elasticsearch_exporter_system_user }}" + _common_config_dir: "{{ elasticsearch_exporter_config_dir }}" + _common_binary_unarchive_opts: ['--strip-components=1'] + tags: + - elasticsearch_exporter_install + +- name: SELinux + ansible.builtin.include_role: + name: prometheus.prometheus._common + tasks_from: selinux.yml + vars: + _common_selinux_port: "{{ elasticsearch_exporter_web_listen_address | urlsplit('port') }}" + when: ansible_facts['selinux'].status == "enabled" + tags: + - elasticsearch_exporter_configure + +- name: Configure + ansible.builtin.include_tasks: + file: configure.yml + tags: + - elasticsearch_exporter_configure + +- name: Ensure Node Exporter is enabled on boot + become: true + ansible.builtin.systemd: + daemon_reload: true + name: elasticsearch_exporter + enabled: true + state: started + when: + - not ansible_check_mode + tags: + - elasticsearch_exporter_run diff --git a/roles/elasticsearch_exporter/tasks/preflight.yml b/roles/elasticsearch_exporter/tasks/preflight.yml new file mode 100644 index 000000000..e08962765 --- /dev/null +++ b/roles/elasticsearch_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: "{{ elasticsearch_exporter_web_listen_address }}" + +- name: Assert that used version supports listen address type + ansible.builtin.assert: + that: + - >- + elasticsearch_exporter_web_listen_address is string + or + ( + elasticsearch_exporter_version is version('1.5.0', '>=') and + elasticsearch_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 elasticsearch_exporter_enabled_collectors" + with_items: "{{ elasticsearch_exporter_disabled_collectors }}" + +- name: Assert that TLS config is correct + when: elasticsearch_exporter_tls_server_config | length > 0 + block: + - name: Assert that TLS key and cert path are set + ansible.builtin.assert: + that: + - "elasticsearch_exporter_tls_server_config.cert_file is defined" + - "elasticsearch_exporter_tls_server_config.key_file is defined" + + - name: Check existence of TLS cert file + ansible.builtin.stat: + path: "{{ elasticsearch_exporter_tls_server_config.cert_file }}" + register: __elasticsearch_exporter_cert_file + + - name: Check existence of TLS key file + ansible.builtin.stat: + path: "{{ elasticsearch_exporter_tls_server_config.key_file }}" + register: __elasticsearch_exporter_key_file + + - name: Assert that TLS key and cert are present + ansible.builtin.assert: + that: + - "__elasticsearch_exporter_cert_file.stat.exists" + - "__elasticsearch_exporter_key_file.stat.exists" + +- name: Discover latest version + ansible.builtin.set_fact: + elasticsearch_exporter_version: "{{ (lookup('url', 'https://api.github.com/repos/{{ _elasticsearch_exporter_repo }}/releases/latest', headers=_github_api_headers, + split_lines=False) | from_json).get('tag_name') | replace('v', '') }}" + run_once: true + until: elasticsearch_exporter_version is version('0.0.0', '>=') + retries: 10 + when: + - elasticsearch_exporter_version == "latest" + tags: + - elasticsearch_exporter + - install + - elasticsearch_exporter_install + - download + - elasticsearch_exporter_download diff --git a/roles/elasticsearch_exporter/templates/elasticsearch_exporter.service.j2 b/roles/elasticsearch_exporter/templates/elasticsearch_exporter.service.j2 new file mode 100644 index 000000000..1bd2906bd --- /dev/null +++ b/roles/elasticsearch_exporter/templates/elasticsearch_exporter.service.j2 @@ -0,0 +1,126 @@ +{{ ansible_managed | comment }} + +# Exposure level `systemd-analyze security`: 2.7 OK + +[Unit] +Description=Prometheus Elasticsearch Exporter +After=network-online.target + +[Service] +Type=simple +User={{ elasticsearch_exporter_system_user }} +Group={{ elasticsearch_exporter_system_group }} +ExecStart={{ elasticsearch_exporter_binary_install_dir }}/elasticsearch_exporter \ +{% if elasticsearch_exporter_tls_server_config | length > 0 or elasticsearch_exporter_http_server_config | length > 0 or elasticsearch_exporter_basic_auth_users | length > 0 %} + {% if elasticsearch_exporter_version is version('1.5.0', '>=') %} + '--web.config.file={{ elasticsearch_exporter_config_dir }}/web_config.yml' \ + {% else %} + '--web.config={{ elasticsearch_exporter_config_dir }}/web_config.yml' \ + {% endif %} +{% endif %} +{% if elasticsearch_exporter_web_disable_exporter_metrics %} + '--web.disable-exporter-metrics' \ +{% endif %} +{% if elasticsearch_exporter_version is version('1.5.0', '>=') and + elasticsearch_exporter_web_listen_address is iterable and + elasticsearch_exporter_web_listen_address is not mapping and + elasticsearch_exporter_web_listen_address is not string %} +{% for address in elasticsearch_exporter_web_listen_address %} + '--web.listen-address={{ address }}' \ +{% endfor %} +{% else %} + '--web.listen-address={{ elasticsearch_exporter_web_listen_address }}' \ +{% endif %} +{% if elasticsearch_exporter_extra_args is iterable and + elasticsearch_exporter_extra_args is not mapping and + elasticsearch_exporter_extra_args is not string %} +{% for item in elasticsearch_exporter_extra_args %} + '{{ item }}' \ +{% endfor %} +{% else %} + '{{ elasticsearch_exporter_extra_args }}' \ +{% endif %} + '--web.telemetry-path={{ elasticsearch_exporter_web_telemetry_path }}' + +SyslogIdentifier=elasticsearch_exporter +Restart=always +RestartSec=1 +StartLimitInterval=0 + +{% set ns = namespace(protect_home = 'yes') %} +{% for m in ansible_facts['mounts'] if m.mount.startswith('/home') %} +{% set ns.protect_home = 'read-only' %} +{% endfor %} +{% if elasticsearch_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 elasticsearch_exporter_cgroups_restriction_enable is defined and elasticsearch_exporter_cgroups_restriction_enable|bool %} +CPUWeight={{ elasticsearch_exporter_cgroups_cpushares | default('80') }} +CPUQuota={{ elasticsearch_exporter_cgroups_cpuquota | default('40%') }} +MemoryMax={{ elasticsearch_exporter_cgroups_memorylimit | default('2G') }} +IOWeight={{ elasticsearch_exporter_cgroups_ioweight | default('80') }} +{% endif %} + +[Install] +WantedBy=multi-user.target diff --git a/roles/elasticsearch_exporter/test-requirements.txt b/roles/elasticsearch_exporter/test-requirements.txt new file mode 100644 index 000000000..7f0b6e759 --- /dev/null +++ b/roles/elasticsearch_exporter/test-requirements.txt @@ -0,0 +1 @@ +bcrypt diff --git a/roles/elasticsearch_exporter/vars/main.yml b/roles/elasticsearch_exporter/vars/main.yml new file mode 100644 index 000000000..33a4fd278 --- /dev/null +++ b/roles/elasticsearch_exporter/vars/main.yml @@ -0,0 +1,9 @@ +--- +_elasticsearch_exporter_go_ansible_arch: "{{ {'i386': '386', + 'x86_64': 'amd64', + 'aarch64': 'arm64', + 'armv7l': 'armv7', + 'armv6l': 'armv6'}.get(ansible_facts['architecture'], ansible_facts['architecture']) }}" +_elasticsearch_exporter_repo: "prometheus-community/elasticsearch_exporter" +_github_api_headers: "{{ {'GITHUB_TOKEN': lookup('ansible.builtin.env', 'GITHUB_TOKEN')} if (lookup('ansible.builtin.env', 'GITHUB_TOKEN')) else {} }}" +_elasticsearch_exporter_binaries: ['elasticsearch_exporter'] diff --git a/tests/integration/targets/molecule-elasticsearch_exporter-alternative/runme.sh b/tests/integration/targets/molecule-elasticsearch_exporter-alternative/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-elasticsearch_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-elasticsearch_exporter-default/runme.sh b/tests/integration/targets/molecule-elasticsearch_exporter-default/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-elasticsearch_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-elasticsearch_exporter-latest/runme.sh b/tests/integration/targets/molecule-elasticsearch_exporter-latest/runme.sh new file mode 100755 index 000000000..d094c3e1b --- /dev/null +++ b/tests/integration/targets/molecule-elasticsearch_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"