From d162a3dbd807f12728b1c7e609b5477c99a3a921 Mon Sep 17 00:00:00 2001
From: paulfantom <pawel@krupa.net.pl>
Date: Wed, 22 Jan 2020 22:31:46 +0100
Subject: [PATCH] add a mechanism to distribute textfile collectors alongside
 node_exporter

---
 README.md                                     | 33 +++++++++++-
 defaults/main.yml                             | 11 ++++
 handlers/main.yml                             |  8 +++
 molecule/alternative/playbook.yml             |  5 ++
 molecule/alternative/prepare.yml              | 18 ++++++-
 .../alternative/tests/test_alternative.py     | 19 ++++++-
 molecule/default/playbook.yml                 |  1 +
 molecule/default/tests/test_default.py        |  3 +-
 tasks/configure.yml                           | 52 +++++++++++++++----
 tasks/preflight.yml                           | 17 ++++++
 templates/cron.service.j2                     |  9 ++++
 templates/cron.timer.j2                       | 14 +++++
 12 files changed, 174 insertions(+), 16 deletions(-)
 create mode 100644 templates/cron.service.j2
 create mode 100644 templates/cron.timer.j2

diff --git a/README.md b/README.md
index 6a261fd..17e3757 100644
--- a/README.md
+++ b/README.md
@@ -27,11 +27,12 @@ All variables which can be overridden are stored in [defaults/main.yml](defaults
 | `node_exporter_web_listen_address` | "0.0.0.0:9100" | Address on which node exporter will listen |
 | `node_exporter_enabled_collectors` | [ systemd, textfile ] | List of additionally enabled collectors. It adds collectors to [those enabled by default](https://github.com/prometheus/node_exporter#enabled-by-default) |
 | `node_exporter_disabled_collectors` | [] | List of disabled collectors. By default node_exporter disables collectors listed [here](https://github.com/prometheus/node_exporter#disabled-by-default). |
-| `node_exporter_textfile_dir` | "/var/lib/node_exporter" | Directory used by the [Textfile Collector](https://github.com/prometheus/node_exporter#textfile-collector). To get permissions to write metrics in this directory, users must be in `node-exp` system group.
+| `node_exporter_textfile_dir` | "/var/lib/node_exporter" | Directory used by the [Textfile Collector](https://github.com/prometheus/node_exporter#textfile-collector). To get permissions to write metrics in this directory, users must be in `node-exp` system group. |
+| `node_exporter_textfile_collectors` | [] | List of textfile collectors which should be copied and crontab setup for them. |
 
 ## Example
 
-### Playbook
+### Basic playbook
 
 Use it in a playbook as follows:
 ```yaml
@@ -40,6 +41,34 @@ Use it in a playbook as follows:
     - cloudalchemy.node-exporter
 ```
 
+### Advanced playbook
+Following playbook sets up few more advanced features of this role. Mostly:
+- Every role run checks and tries to update node_exporter binary to latest available version
+- node_exporter is run as `root` user, which allows to use `systemd` collector
+- `systemd` collector is enabled
+- enables cron job running two textfile collectors: [`yum.sh`](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/yum.sh) and [`md_info_detail.sh`](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/md_info_detail.sh) Textfile collector is also copied from deployer host with path specified in `src`.
+
+```yaml
+- hosts: all
+  roles:
+    - cloudalchemy.node-exporter
+  vars:
+    node_exporter_system_group: "root"
+    node_exporter_system_user: "root"
+    node_exporter_enabled_collectors:
+      - systemd
+    node_exporter_textfile_collectors:
+      - src: "/tmp/md_info_detail.sh"
+        special_time: "hourly"
+      - src: "/tmp/yum.sh"
+        user: node-exp
+        minute: "*"
+        hour: "*"
+        day: "*"
+        month: "*"
+        weekday: "*"
+```
+
 ### Demo site
 
 We provide demo site for full monitoring solution based on prometheus and grafana. Repository with code and links to running instances is [available on github](https://github.com/cloudalchemy/demo-site) and site is hosted on [DigitalOcean](https://digitalocean.com).
diff --git a/defaults/main.yml b/defaults/main.yml
index f6f549a..dc2b75f 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -4,6 +4,17 @@ node_exporter_binary_local_dir: ""
 node_exporter_web_listen_address: "0.0.0.0:9100"
 
 node_exporter_textfile_dir: "/var/lib/node_exporter"
+node_exporter_textfile_collectors: []
+# node_exporter_textfile_collectors:
+#   - src: "/tmp/apt.sh"
+#     user: root
+#     calendar: ""
+#     active_sec: ""
+#     boot_sec: ""
+#     startup_sec: ""
+#     unit_active_sec: ""
+#     unit_inactive_sec: ""
+
 
 node_exporter_enabled_collectors:
   - systemd
diff --git a/handlers/main.yml b/handlers/main.yml
index 25551ee..2c4240c 100644
--- a/handlers/main.yml
+++ b/handlers/main.yml
@@ -5,3 +5,11 @@
     daemon_reload: true
     name: node_exporter
     state: restarted
+
+- name: reload cron
+  become: true
+  systemd:
+    daemon_reload: true
+    name: "textfile-collector-{{ item.src | basename }}.timer"
+    state: restarted
+  with_items: "{{ node_exporter_textfile_collectors }}"
diff --git a/molecule/alternative/playbook.yml b/molecule/alternative/playbook.yml
index ce51111..22c455e 100644
--- a/molecule/alternative/playbook.yml
+++ b/molecule/alternative/playbook.yml
@@ -11,3 +11,8 @@
       - entropy
     node_exporter_disabled_collectors:
       - diskstats
+    node_exporter_textfile_collectors:
+      - src: "/tmp/apt.sh"
+        user: root
+        calendar: "1h 30min"
+        boot_sec: 90
diff --git a/molecule/alternative/prepare.yml b/molecule/alternative/prepare.yml
index 2f75da1..742234c 100644
--- a/molecule/alternative/prepare.yml
+++ b/molecule/alternative/prepare.yml
@@ -1,5 +1,5 @@
 ---
-- name: Prepare
+- name: Prepare localhost
   hosts: localhost
   gather_facts: false
   vars:
@@ -35,3 +35,19 @@
         state: link
       run_once: true
       check_mode: false
+
+    - name: get collector scripts
+      become: false
+      get_url:
+        url: "{{ item }}"
+        dest: "/tmp/{{ item.split('/')[-1] }}"
+      with_items:
+        - https://raw.githubusercontent.com/prometheus-community/node-exporter-textfile-collector-scripts/master/apt.sh
+
+- name: Prepare instances
+  hosts: all
+  tasks:
+    - name: Install cron
+      package:
+        name: "{{ 'cronie' if (ansible_os_family | lower == 'redhat') else 'cron' }}"
+        state: present
diff --git a/molecule/alternative/tests/test_alternative.py b/molecule/alternative/tests/test_alternative.py
index 87cfc15..1ea16fe 100644
--- a/molecule/alternative/tests/test_alternative.py
+++ b/molecule/alternative/tests/test_alternative.py
@@ -5,13 +5,30 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
     os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
 
 
+def test_files(host):
+    files = [
+        "/usr/local/bin/md_info_detail.sh",
+        "/usr/local/bin/apt.sh"
+        "/etc/systemd/system/textfile-collector-apt.sh.service"
+        "/etc/systemd/system/textfile-collector-apt.sh.timer"
+        "/etc/systemd/system/textfile-collector-md_info_detail.sh.service"
+        "/etc/systemd/system/textfile-collector-md_info_detail.sh.timer"
+    ]
+    for file in files:
+        f = host.file(file)
+        assert f.exists
+        assert f.is_file
+        assert oct(f.mode) == '0o755'
+
+
 def test_directories(host):
     dirs = [
         "/var/lib/node_exporter"
     ]
     for dir in dirs:
         d = host.file(dir)
-        assert not d.exists
+        assert d.is_directory
+        assert d.exists
 
 
 def test_service(host):
diff --git a/molecule/default/playbook.yml b/molecule/default/playbook.yml
index 1e92855..36af894 100644
--- a/molecule/default/playbook.yml
+++ b/molecule/default/playbook.yml
@@ -5,3 +5,4 @@
     - ansible-node-exporter
   vars:
     node_exporter_web_listen_address: "127.0.0.1:9100"
+    node_exporter_textfile_dir: ""
diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py
index 7797588..ef1a546 100644
--- a/molecule/default/tests/test_default.py
+++ b/molecule/default/tests/test_default.py
@@ -11,8 +11,7 @@ def test_directories(host):
     ]
     for dir in dirs:
         d = host.file(dir)
-        assert d.is_directory
-        assert d.exists
+        assert not d.exists
 
 
 def test_files(host):
diff --git a/tasks/configure.yml b/tasks/configure.yml
index 9e9f9bc..ec9d826 100644
--- a/tasks/configure.yml
+++ b/tasks/configure.yml
@@ -8,16 +8,6 @@
     mode: 0644
   notify: restart node_exporter
 
-- name: Create textfile collector dir
-  file:
-    path: "{{ node_exporter_textfile_dir }}"
-    state: directory
-    owner: "{{ _node_exporter_system_user }}"
-    group: "{{ _node_exporter_system_group }}"
-    recurse: true
-    mode: u+rwX,g+rwX,o=rX
-  when: node_exporter_textfile_dir | length > 0
-
 - name: Allow Node Exporter port in SELinux on RedHat OS family
   seport:
     ports: "{{ node_exporter_web_listen_address.split(':')[-1] }}"
@@ -27,3 +17,45 @@
   when:
     - ansible_version.full is version_compare('2.4', '>=')
     - ansible_selinux.status == "enabled"
+
+- block:
+    - name: Create textfile collector dir
+      file:
+        path: "{{ node_exporter_textfile_dir }}"
+        state: directory
+        owner: "{{ _node_exporter_system_user }}"
+        group: "{{ _node_exporter_system_group }}"
+        recurse: true
+        mode: u+rwX,g+rwX,o=rX
+
+    - name: Download textfile collector scripts
+      copy:
+        src: "{{ item.src }}"
+        dest: "{{ _node_exporter_binary_install_dir }}/{{ item.src | basename }}"
+        owner: root
+        group: root
+        mode: "0755"
+      with_items: "{{ node_exporter_textfile_collectors }}"
+
+    - name: Setup textfile collector script systemd cron service
+      template:
+        src: "cron.service.j2"
+        dest: "/etc/systemd/systemd/textfile-collector-{{ item.src | basename }}.service"
+      with_items: "{{ node_exporter_textfile_collectors }}"
+      notify: reload cron
+
+    - name: Setup textfile collector script systemd cron timer
+      template:
+        src: "cron.timer.j2"
+        dest: "/etc/systemd/systemd/textfile-collector-{{ item.src | basename }}.timer"
+      with_items: "{{ node_exporter_textfile_collectors }}"
+      notify: reload cron
+
+    - name: Enable textfile collector
+      systemd:
+        daemon_reload: true
+        name: "textfile-collector-{{ item.src | basename }}.timer"
+        enabled: true
+        state: started
+      with_items: "{{ node_exporter_textfile_collectors }}"
+  when: node_exporter_textfile_dir | length > 0
diff --git a/tasks/preflight.yml b/tasks/preflight.yml
index 79fff42..ea2ca87 100644
--- a/tasks/preflight.yml
+++ b/tasks/preflight.yml
@@ -27,6 +27,23 @@
       - "item not in node_exporter_enabled_collectors"
   with_items: "{{ node_exporter_disabled_collectors }}"
 
+- block:
+    - name: Check for "/etc/crontab"
+      stat:
+        path: "/etc/crontab"
+      register: __node_exportert_etc_crontab
+
+    - name: Assert /etc/crontab exists when using textfile collector scripts
+      assert:
+        that:
+          __node_exportert_etc_crontab.stat.exists
+
+    - name: Assert node_exporter_textfile_dir is not empty when using collector scripts
+      assert:
+        that:
+          - node_exporter_textfile_dir | length != 0
+  when: node_exporter_textfile_collectors | length != 0
+
 - name: Check if node_exporter is installed
   stat:
     path: "{{ _node_exporter_binary_install_dir }}/node_exporter"
diff --git a/templates/cron.service.j2 b/templates/cron.service.j2
new file mode 100644
index 0000000..9830a5f
--- /dev/null
+++ b/templates/cron.service.j2
@@ -0,0 +1,9 @@
+[Unit]
+Description=Run Textfile Collector {{ item.src | basename }}
+
+[Service]
+User={{ item.user | default("root") }}
+ExecStart=/bin/sh -c '\
+  TEMP=$(mktemp -q) && \
+  {{ _node_exporter_binary_install_dir }}/{{ item.src | basename }} > $TEMP && \
+  mv $TEMP {{ node_exporter_textfile_dir }}/{{ item.src | basename }}.prom'
diff --git a/templates/cron.timer.j2 b/templates/cron.timer.j2
new file mode 100644
index 0000000..52dbdd5
--- /dev/null
+++ b/templates/cron.timer.j2
@@ -0,0 +1,14 @@
+[Unit]
+Description=Textfile Collector {{ item.src | basename }} timer
+
+[Timer]
+Unit=textfile-collector-{{ item.src | basename }}.service
+OnCalendar={{ item.calendar }}
+OnBootSec={{ item.boot_sec }}
+OnActiveSec={{ item.active_sec }}
+OnStartupSec={{ item.startup_sec }}
+OnUnitActiveSec={{ item.unit_active_sec }}
+OnUnitInactiveSec={{ item.unit_inactive_sec }}
+
+[Install]
+WantedBy=multi-user.target
-- 
GitLab