From d728ed0b3e7735db6b1fd4c4c7bf394630286d50 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 14:56:42 +0300 Subject: [PATCH 01/96] add sergey malyuk in authors --- galaxy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/galaxy.yml b/galaxy.yml index 66c7b7e..b17cb19 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -5,6 +5,7 @@ version: 0.0.1 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) + - Sergey Malyuk (malyuk.ss@genlab.llc) description: >- The genlab.common collection includes roles for performing deploying and configuring common infrastructural components license_file: LICENSE From 17ebbf1fcaf6145a37e378fa78103a6b353a850f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 15:11:23 +0300 Subject: [PATCH 02/96] add alertmanager role --- roles/alertmanager/README.md | 53 ++++++++++++ roles/alertmanager/defaults/main.yml | 7 ++ roles/alertmanager/handlers/main.yml | 7 ++ roles/alertmanager/meta/main.yml | 17 ++++ .../default/alertmanager/alertmanager.yml | 36 ++++++++ .../molecule/default/converge.yml | 6 ++ .../molecule/default/molecule.yml | 27 ++++++ .../alertmanager/molecule/default/verify.yml | 55 ++++++++++++ roles/alertmanager/tasks/configuration.yml | 24 ++++++ roles/alertmanager/tasks/install.yml | 85 +++++++++++++++++++ roles/alertmanager/tasks/main.yml | 9 ++ .../templates/alertmanager.service.j2 | 27 ++++++ roles/alertmanager/vars/main.yml | 1 + 13 files changed, 354 insertions(+) create mode 100644 roles/alertmanager/README.md create mode 100644 roles/alertmanager/defaults/main.yml create mode 100644 roles/alertmanager/handlers/main.yml create mode 100644 roles/alertmanager/meta/main.yml create mode 100644 roles/alertmanager/molecule/default/alertmanager/alertmanager.yml create mode 100644 roles/alertmanager/molecule/default/converge.yml create mode 100644 roles/alertmanager/molecule/default/molecule.yml create mode 100644 roles/alertmanager/molecule/default/verify.yml create mode 100644 roles/alertmanager/tasks/configuration.yml create mode 100644 roles/alertmanager/tasks/install.yml create mode 100644 roles/alertmanager/tasks/main.yml create mode 100644 roles/alertmanager/templates/alertmanager.service.j2 create mode 100644 roles/alertmanager/vars/main.yml diff --git a/roles/alertmanager/README.md b/roles/alertmanager/README.md new file mode 100644 index 0000000..a8a57da --- /dev/null +++ b/roles/alertmanager/README.md @@ -0,0 +1,53 @@ +genlab.alertmanager +========= + +The [Alertmanager](https://github.com/prometheus/alertmanager) handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts. + +You'll need to manually specify the paths to your template files in the main Alertmanager configuration file (`alertmanager.yml`) on the target machine. The configuration file must have a `.yml` suffix, and template files must use a `.tmpl` suffix. + +Requirements +------------ + +None + +Role Variables +-------------- + +``` +alertmanager_version: 0.28.1 # version of Alertmanager (AM) +config_dir: "/etc/alertmanager/conf" # where to place AM configs +storage_dir: "/var/lib/alertmanager" # path to AM storage +alertmanager_dir: "/etc/alertmanager" # where to install AM on target +alertmanager_user: "alertmanager" # system user name +alertmanager_group: "alertmanager" # system group name +config_source_dir: "./alertmanager" # path to config files on source + +# Optional +template_source_dir: "./templates" # path to template files on source +``` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.template + config_source_dir: alertmanager + alertmanager_version: 0.28.1 + +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/alertmanager/defaults/main.yml b/roles/alertmanager/defaults/main.yml new file mode 100644 index 0000000..88707b3 --- /dev/null +++ b/roles/alertmanager/defaults/main.yml @@ -0,0 +1,7 @@ +--- +alertmanager_version: 0.28.1 +config_dir: "/etc/alertmanager/conf" +storage_dir: "/var/lib/alertmanager" +alertmanager_dir: "/etc/alertmanager" +alertmanager_user: alertmanager +alertmanager_group: alertmanager diff --git a/roles/alertmanager/handlers/main.yml b/roles/alertmanager/handlers/main.yml new file mode 100644 index 0000000..4a0e813 --- /dev/null +++ b/roles/alertmanager/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "(Re)start and enable Alertmanager" + ansible.builtin.systemd_service: + name: alertmanager.service + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/alertmanager/meta/main.yml b/roles/alertmanager/meta/main.yml new file mode 100644 index 0000000..942b852 --- /dev/null +++ b/roles/alertmanager/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "alertmanager" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml b/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml new file mode 100644 index 0000000..7c3bff0 --- /dev/null +++ b/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml @@ -0,0 +1,36 @@ +global: + # SMTP configuration for email notifications (if needed) + smtp_smarthost: 'mailserver.example.com:587' + smtp_from: 'alertmanager@example.com' + smtp_auth_username: 'alertmanager' + smtp_auth_password: 'your_password' + + # Other global settings like resolve_timeout, http_config, etc. + +route: + # Default receiver for alerts + receiver: 'default-receiver' + + # Labels used for grouping alerts + group_by: ['alertname', 'instance', 'severity'] + + # Timing settings (group_wait, group_interval, repeat_interval) + # You can have nested 'routes' for more complex routing logic + +receivers: + - name: 'default-receiver' + email_configs: + - to: 'ops-team@example.com' + +inhibit_rules: + # Rules to suppress alerts based on other alerts + # Example: + - source_match: + severity: 'critical' + target_match: + severity: 'warning' + # Suppress 'warning' alerts if a 'critical' alert is also firing + +templates: + # Paths to template files for customizing notifications + - '/etc/alertmanager/templates/*.tmpl' \ No newline at end of file diff --git a/roles/alertmanager/molecule/default/converge.yml b/roles/alertmanager/molecule/default/converge.yml new file mode 100644 index 0000000..3f7be92 --- /dev/null +++ b/roles/alertmanager/molecule/default/converge.yml @@ -0,0 +1,6 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.alertmanager + config_source_dir: alertmanager diff --git a/roles/alertmanager/molecule/default/molecule.yml b/roles/alertmanager/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/alertmanager/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/alertmanager/molecule/default/verify.yml b/roles/alertmanager/molecule/default/verify.yml new file mode 100644 index 0000000..0836d76 --- /dev/null +++ b/roles/alertmanager/molecule/default/verify.yml @@ -0,0 +1,55 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Check if Alertmanager is installed" + changed_when: false + ansible.builtin.command: "alertmanager --version" + register: alertmanager_installed_version + + - name: "Check Alertmanager version" + ansible.builtin.assert: + that: "alertmanager_installed_version.stdout is regex('{{ alertmanager_version }}')" + success_msg: "Alertmanager version {{ alertmanager_version }} is installed and working" + fail_msg: "Alertmanager version {{ alertmanager_version }} is not installed or not working correctly" + + # kics-scan ignore-block + - name: "Check if Alertmanager is reachable" + ansible.builtin.uri: + url: "http://localhost:9093/-/healthy" + return_content: true + status_code: 200 + method: GET + body_format: json + register: alertmanager_health + + - name: "Assert Alertmanager health status" + ansible.builtin.assert: + that: "alertmanager_health.content == 'OK'" + success_msg: "Alertmanager is healthy" + fail_msg: "Alertmanager is not healthy" + + # kics-scan ignore-block + - name: "Check if Alertmanager is ready" + ansible.builtin.uri: + url: "http://localhost:9093/-/ready" + return_content: true + status_code: 200 + method: GET + body_format: json + register: alertmanager_ready + + - name: "Assert Alertmanager readiness status" + ansible.builtin.assert: + that: "alertmanager_ready.content == 'OK'" + success_msg: "Alertmanager is ready" + fail_msg: "Alertmanager is not ready" diff --git a/roles/alertmanager/tasks/configuration.yml b/roles/alertmanager/tasks/configuration.yml new file mode 100644 index 0000000..4932c8e --- /dev/null +++ b/roles/alertmanager/tasks/configuration.yml @@ -0,0 +1,24 @@ +--- + +- name: "Copy config file" + notify: "(Re)start and enable Alertmanager" + ansible.builtin.template: + src: "{{ item }}" + dest: "{{ config_dir }}/{{ item | basename }}" + owner: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + mode: "0660" + with_fileglob: + - "{{ config_source_dir }}/*.yml" + +- name: "Copy templates if existed" + notify: "(Re)start and enable Alertmanager" + when: template_source_dir is defined + ansible.builtin.copy: + src: "{{ item }}" + dest: "{{ config_dir }}/{{ item | basename }}" + owner: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + mode: "0660" + with_fileglob: + - "{{ template_source_dir }}/*.tmpl" diff --git a/roles/alertmanager/tasks/install.yml b/roles/alertmanager/tasks/install.yml new file mode 100644 index 0000000..f97a99e --- /dev/null +++ b/roles/alertmanager/tasks/install.yml @@ -0,0 +1,85 @@ +--- +- name: "Create Alertmanager system group" + ansible.builtin.group: + name: "{{ alertmanager_user }}" + system: true + state: present + +- name: "Create Alertmanager system user" + ansible.builtin.user: + name: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + system: true + shell: "/sbin/nologin" + create_home: false + state: present + +- name: "Install Alertmanager from binary" + block: + - name: "Check Alertmanager version" + changed_when: false + ansible.builtin.command: + cmd: "alertmanager --version" + register: alertmanager_ver + + - name: "Assert version correctness" + ansible.builtin.assert: + that: "alertmanager_ver.stdout is regex('{{ alertmanager_version }}')" + success_msg: "alertmanager version {{ alertmanager_version }} is installed and working" + fail_msg: "alertmanager version {{ alertmanager_version }} is not installed or not working correctly" + + rescue: + - name: "Create Alertmanager directories '{{ item }}'" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + mode: "0755" + with_items: + - "{{ config_dir }}" + - "{{ alertmanager_dir }}" + - "{{ storage_dir }}" + + - name: "Download Alertmanager binary" + ansible.builtin.get_url: + url: "https://github.com/prometheus/alertmanager/releases/download/v{{ alertmanager_version }}/\ + alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz" + dest: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz" + owner: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + mode: "0644" + + - name: "Unpack Alertmanager binaries" + notify: "(Re)start and enable Alertmanager" + ansible.builtin.unarchive: + src: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz" + dest: "{{ alertmanager_dir }}" + creates: "{{ alertmanager_dir }}/alertmanager-{{ alertmanager_version }}.linux-amd64" + remote_src: true + + - name: "Cleanup downloaded file" + ansible.builtin.file: + path: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz" + state: absent + + - name: "Move official alertmanager and amtool binaries" + ansible.builtin.copy: + src: "{{ alertmanager_dir }}/alertmanager-{{ alertmanager_version }}.linux-amd64/{{ item }}" + dest: "/usr/local/bin/{{ item }}" + mode: "0755" + owner: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + remote_src: true + with_items: + - alertmanager + - amtool + + +- name: "Create systemd service unit" + ansible.builtin.template: + src: alertmanager.service.j2 + dest: /etc/systemd/system/alertmanager.service + owner: "{{ alertmanager_user }}" + group: "{{ alertmanager_group }}" + mode: "0660" diff --git a/roles/alertmanager/tasks/main.yml b/roles/alertmanager/tasks/main.yml new file mode 100644 index 0000000..6bd62b2 --- /dev/null +++ b/roles/alertmanager/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: "Run installation tasks" + ansible.builtin.include_tasks: install.yml + +- name: "Run configuration tasks" + ansible.builtin.include_tasks: configuration.yml + +- name: "Flush handlers" + ansible.builtin.meta: "flush_handlers" diff --git a/roles/alertmanager/templates/alertmanager.service.j2 b/roles/alertmanager/templates/alertmanager.service.j2 new file mode 100644 index 0000000..e4ee45a --- /dev/null +++ b/roles/alertmanager/templates/alertmanager.service.j2 @@ -0,0 +1,27 @@ +[Unit] +Description=Alertmanager Service +After=network.target +Documentation="https://prometheus.io/docs/alerting/latest/alertmanager/" + +[Service] +User={{ alertmanager_user }} +Group={{ alertmanager_group }} +Type=simple +ExecStart=/usr/local/bin/alertmanager \ + --config.file={{ config_dir }}/alertmanager.yml \ + --storage.path={{ storage_dir }} +Restart=on-failure + +# Security hardening +ReadWritePaths={{ storage_dir }} +ProtectSystem=strict +NoNewPrivileges=true +PrivateTmp=true +ProtectKernelModules=true +ProtectControlGroups=true +ProtectKernelTunables=true +ProtectClock=yes +RestrictSUIDSGID=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/alertmanager/vars/main.yml b/roles/alertmanager/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/alertmanager/vars/main.yml @@ -0,0 +1 @@ +--- From 27ce34c42007c0d663c2c1bf2df53356b1ed5ffd Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 15:29:02 +0300 Subject: [PATCH 03/96] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dc6784a..eecffb1 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ ## Roles - [mount_device](roles/mount_device/README.md) +- [alertmanager](roles/alertmanager/README.md) From 48ced598f75a87759d3c6ddc0da5f27a151bca2a Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 15:29:16 +0300 Subject: [PATCH 04/96] add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..feaf22e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.ansible \ No newline at end of file From 56436fe607f4a8466e6e7cb0388ff6f4a55f5189 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 15:35:57 +0300 Subject: [PATCH 05/96] fix linter errors --- roles/alertmanager/README.md | 4 ++-- roles/alertmanager/defaults/main.yml | 4 ++-- roles/alertmanager/meta/main.yml | 6 +++--- .../default/alertmanager/alertmanager.yml | 18 +++++++++--------- .../alertmanager/molecule/default/converge.yml | 2 +- roles/alertmanager/molecule/default/verify.yml | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/roles/alertmanager/README.md b/roles/alertmanager/README.md index a8a57da..7ef20e9 100644 --- a/roles/alertmanager/README.md +++ b/roles/alertmanager/README.md @@ -15,8 +15,8 @@ Role Variables ``` alertmanager_version: 0.28.1 # version of Alertmanager (AM) -config_dir: "/etc/alertmanager/conf" # where to place AM configs -storage_dir: "/var/lib/alertmanager" # path to AM storage +alertmanager_config_dir: "/etc/alertmanager/conf" # where to place AM configs +alertmanager_storage_dir: "/var/lib/alertmanager" # path to AM storage alertmanager_dir: "/etc/alertmanager" # where to install AM on target alertmanager_user: "alertmanager" # system user name alertmanager_group: "alertmanager" # system group name diff --git a/roles/alertmanager/defaults/main.yml b/roles/alertmanager/defaults/main.yml index 88707b3..4e14295 100644 --- a/roles/alertmanager/defaults/main.yml +++ b/roles/alertmanager/defaults/main.yml @@ -1,7 +1,7 @@ --- alertmanager_version: 0.28.1 -config_dir: "/etc/alertmanager/conf" -storage_dir: "/var/lib/alertmanager" +alertmanager_config_dir: "/etc/alertmanager/conf" +alertmanager_storage_dir: "/var/lib/alertmanager" alertmanager_dir: "/etc/alertmanager" alertmanager_user: alertmanager alertmanager_group: alertmanager diff --git a/roles/alertmanager/meta/main.yml b/roles/alertmanager/meta/main.yml index 942b852..0cebb5a 100644 --- a/roles/alertmanager/meta/main.yml +++ b/roles/alertmanager/meta/main.yml @@ -1,6 +1,6 @@ --- galaxy_info: - role_name: "alertmanager" + role_name: alertmanager namespace: genlab author: "Alexander Gorelyshev" company: "Genlab, LLC" @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml b/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml index 7c3bff0..78e14ea 100644 --- a/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml +++ b/roles/alertmanager/molecule/default/alertmanager/alertmanager.yml @@ -7,20 +7,20 @@ global: # Other global settings like resolve_timeout, http_config, etc. -route: +route: # Default receiver for alerts receiver: 'default-receiver' - # Labels used for grouping alerts - group_by: ['alertname', 'instance', 'severity'] + # Labels used for grouping alerts + group_by: ['alertname', 'instance', 'severity'] - # Timing settings (group_wait, group_interval, repeat_interval) - # You can have nested 'routes' for more complex routing logic + # Timing settings (group_wait, group_interval, repeat_interval) + # You can have nested 'routes' for more complex routing logic receivers: - name: 'default-receiver' email_configs: - - to: 'ops-team@example.com' + - to: 'ops-team@example.com' inhibit_rules: # Rules to suppress alerts based on other alerts @@ -31,6 +31,6 @@ inhibit_rules: severity: 'warning' # Suppress 'warning' alerts if a 'critical' alert is also firing -templates: - # Paths to template files for customizing notifications - - '/etc/alertmanager/templates/*.tmpl' \ No newline at end of file +templates: + # Paths to template files for customizing notifications + - '/etc/alertmanager/templates/*.tmpl' diff --git a/roles/alertmanager/molecule/default/converge.yml b/roles/alertmanager/molecule/default/converge.yml index 3f7be92..470c151 100644 --- a/roles/alertmanager/molecule/default/converge.yml +++ b/roles/alertmanager/molecule/default/converge.yml @@ -2,5 +2,5 @@ - name: Converge hosts: all roles: - - role: genlab.alertmanager + - role: genlab.common.alertmanager config_source_dir: alertmanager diff --git a/roles/alertmanager/molecule/default/verify.yml b/roles/alertmanager/molecule/default/verify.yml index 0836d76..37eb46f 100644 --- a/roles/alertmanager/molecule/default/verify.yml +++ b/roles/alertmanager/molecule/default/verify.yml @@ -9,7 +9,7 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Check if Alertmanager is installed" changed_when: false From 756b5904373a2c017004b39f6ef0c568e5d7b51e Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 16:44:48 +0300 Subject: [PATCH 06/96] update variables --- roles/alertmanager/tasks/configuration.yml | 4 ++-- roles/alertmanager/tasks/install.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/roles/alertmanager/tasks/configuration.yml b/roles/alertmanager/tasks/configuration.yml index 4932c8e..f06c996 100644 --- a/roles/alertmanager/tasks/configuration.yml +++ b/roles/alertmanager/tasks/configuration.yml @@ -4,7 +4,7 @@ notify: "(Re)start and enable Alertmanager" ansible.builtin.template: src: "{{ item }}" - dest: "{{ config_dir }}/{{ item | basename }}" + dest: "{{ alertmanager_config_dir }}/{{ item | basename }}" owner: "{{ alertmanager_user }}" group: "{{ alertmanager_group }}" mode: "0660" @@ -16,7 +16,7 @@ when: template_source_dir is defined ansible.builtin.copy: src: "{{ item }}" - dest: "{{ config_dir }}/{{ item | basename }}" + dest: "{{ alertmanager_config_dir }}/{{ item | basename }}" owner: "{{ alertmanager_user }}" group: "{{ alertmanager_group }}" mode: "0660" diff --git a/roles/alertmanager/tasks/install.yml b/roles/alertmanager/tasks/install.yml index f97a99e..db0b8c6 100644 --- a/roles/alertmanager/tasks/install.yml +++ b/roles/alertmanager/tasks/install.yml @@ -37,9 +37,9 @@ group: "{{ alertmanager_group }}" mode: "0755" with_items: - - "{{ config_dir }}" + - "{{ alertmanager_config_dir }}" - "{{ alertmanager_dir }}" - - "{{ storage_dir }}" + - "{{ alertmanager_storage_dir }}" - name: "Download Alertmanager binary" ansible.builtin.get_url: From ff206814345fe01326461f67dd3db4d52e99d5e3 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 8 Dec 2025 16:48:41 +0300 Subject: [PATCH 07/96] update variables --- roles/alertmanager/templates/alertmanager.service.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/alertmanager/templates/alertmanager.service.j2 b/roles/alertmanager/templates/alertmanager.service.j2 index e4ee45a..a85ae68 100644 --- a/roles/alertmanager/templates/alertmanager.service.j2 +++ b/roles/alertmanager/templates/alertmanager.service.j2 @@ -8,12 +8,12 @@ User={{ alertmanager_user }} Group={{ alertmanager_group }} Type=simple ExecStart=/usr/local/bin/alertmanager \ - --config.file={{ config_dir }}/alertmanager.yml \ - --storage.path={{ storage_dir }} + --config.file={{ alertmanager_config_dir }}/alertmanager.yml \ + --storage.path={{ alertmanager_storage_dir }} Restart=on-failure # Security hardening -ReadWritePaths={{ storage_dir }} +ReadWritePaths={{ alertmanager_storage_dir }} ProtectSystem=strict NoNewPrivileges=true PrivateTmp=true From 2902ab08b3bf06a28789a46ac29fdf9fa705b400 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 9 Dec 2025 17:05:08 +0300 Subject: [PATCH 08/96] add .vscode to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index feaf22e..1a7d3a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.ansible \ No newline at end of file +.ansible +.vscode \ No newline at end of file From 19989e72d84e090b029404b54526bfcda87cd500 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:13:00 +0300 Subject: [PATCH 09/96] add wg_hub role with fixed ansible-lint errors --- roles/wg_hub/README.md | 43 +++++++++++++++++ roles/wg_hub/defaults/main.yml | 7 +++ roles/wg_hub/handlers/main.yml | 7 +++ roles/wg_hub/meta/main.yml | 17 +++++++ roles/wg_hub/molecule/default/converge.yml | 24 ++++++++++ roles/wg_hub/molecule/default/molecule.yml | 27 +++++++++++ roles/wg_hub/molecule/default/verify.yml | 13 ++++++ roles/wg_hub/tasks/main.yml | 54 ++++++++++++++++++++++ roles/wg_hub/vars/main.yml | 1 + 9 files changed, 193 insertions(+) create mode 100644 roles/wg_hub/README.md create mode 100644 roles/wg_hub/defaults/main.yml create mode 100644 roles/wg_hub/handlers/main.yml create mode 100644 roles/wg_hub/meta/main.yml create mode 100644 roles/wg_hub/molecule/default/converge.yml create mode 100644 roles/wg_hub/molecule/default/molecule.yml create mode 100644 roles/wg_hub/molecule/default/verify.yml create mode 100644 roles/wg_hub/tasks/main.yml create mode 100644 roles/wg_hub/vars/main.yml diff --git a/roles/wg_hub/README.md b/roles/wg_hub/README.md new file mode 100644 index 0000000..fe551c0 --- /dev/null +++ b/roles/wg_hub/README.md @@ -0,0 +1,43 @@ +template +========= + +Template for Ansible role monorepos. + +⚠️ Do not forget to update: + +- `meta/main.yml` +- Conda/Mamba manifests +- this README =) + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.template +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/wg_hub/defaults/main.yml b/roles/wg_hub/defaults/main.yml new file mode 100644 index 0000000..7bf9b28 --- /dev/null +++ b/roles/wg_hub/defaults/main.yml @@ -0,0 +1,7 @@ +--- +wg_hub_iface_name: wg0 +wg_hub_host_id: hub +wg_hub_ipv4_vpn_addr: 10.0.0.254 +wg_hub_ipv4_vpn_cidr: 24 +wg_hub_wg_port: 51820 +wg_hub_hide_secrets: true diff --git a/roles/wg_hub/handlers/main.yml b/roles/wg_hub/handlers/main.yml new file mode 100644 index 0000000..ff2cedb --- /dev/null +++ b/roles/wg_hub/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "Run the Wireguard service" + ansible.builtin.systemd_service: + name: wg-quick@{{ wg_hub_iface_name | replace("-", "_") }} + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/wg_hub/meta/main.yml b/roles/wg_hub/meta/main.yml new file mode 100644 index 0000000..e2da64a --- /dev/null +++ b/roles/wg_hub/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "wg_hub" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["jammy", "noble"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/wg_hub/molecule/default/converge.yml b/roles/wg_hub/molecule/default/converge.yml new file mode 100644 index 0000000..e807dae --- /dev/null +++ b/roles/wg_hub/molecule/default/converge.yml @@ -0,0 +1,24 @@ +--- +- name: Converge + hosts: all + vars: + wg_hub_iface_name: adm + wg_hub_ipv4_vpn_addr: 10.0.0.254 + wg_hub_ipv4_vpn_cidr: 24 + wg_hub_wg_port: 51820 + wg_hub_wg_pkey: gABk6e/n3UDgudEVlUrEVcVdn0tc0YVDeCASsI10QFA= + wg_hub_wg_pubkey: EvcoZ21/p0AHz5y95jwjVIR9puljc2kXqh/f3U1A4nk= + wg_hub_hide_secrets: false + # dns_server: 127.0.0.1:5300 # doesn't work in Github Actions environment + peers: + - wg_hub_host_id: alpha + wg_hub_ipv4_vpn_addr: 10.0.0.1 + wg_hub_wg_pubkey: LfEJgNiJ05nx4nWB0Pj3wS3WRyFq567fsdDh4XZqRF0= + wg_psk: i3JCrQOfptZhgpL+BTm/65MPW/ljJexCgiuWMIqZJYo= + - wg_hub_host_id: beta + wg_hub_ipv4_vpn_addr: 10.0.0.2 + wg_hub_wg_pubkey: oq3Fcwwfxsi5f5UHcZKtxMwQ2aSeHOUe3r35soUUYzU= + wg_psk: Z/z7Qo8hW97UcImYE/ZbCxpNmizfVhvl0dzygtvtYYg= + + roles: + - role: genlab.common.wg_hub diff --git a/roles/wg_hub/molecule/default/molecule.yml b/roles/wg_hub/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/wg_hub/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/wg_hub/molecule/default/verify.yml b/roles/wg_hub/molecule/default/verify.yml new file mode 100644 index 0000000..38f93ff --- /dev/null +++ b/roles/wg_hub/molecule/default/verify.yml @@ -0,0 +1,13 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + iface_name: adm + + tasks: + - name: "Check that the Wireguard service is running" + ansible.builtin.service: + name: wg-quick@{{ iface_name }} + state: started diff --git a/roles/wg_hub/tasks/main.yml b/roles/wg_hub/tasks/main.yml new file mode 100644 index 0000000..78551ea --- /dev/null +++ b/roles/wg_hub/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: "Install wireguard system-wide" + ansible.builtin.apt: + name: wireguard + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Ensure no dashes in the interface name" + when: "'-' in wg_hub_iface_name" + ansible.builtin.fail: + msg: "The interface name must not contain dashes, got: '{{ wg_hub_iface_name }}'" + +- name: "Create the Hub configuration file" + no_log: "{{ wg_hub_hide_secrets }}" + notify: "Run the Wireguard service" + ansible.builtin.blockinfile: + path: "/etc/wireguard/{{ wg_hub_iface_name }}.conf" + create: true + owner: root + group: root + mode: "0600" + state: present + block: | + [Interface] + Address = {{ wg_hub_ipv4_vpn_addr }}/32 + ListenPort = {{ wg_hub_wg_port }} + PrivateKey = {{ wg_hub_wg_pkey }} + + PreUp = sysctl -w net.ipv4.ip_forward=1 + {% if dns_server is defined %} + PostUp = resolvectl dns %i {{ dns_server }}; resolvectl domain %i {{ wg_hub_iface_name }}.local + {% endif %} + PostDown = sysctl -w net.ipv4.ip_forward=0 + +- name: "Add [Peer] sections to the Hub configuration file" + no_log: "{{ wg_hub_hide_secrets }}" + notify: "Run the Wireguard service" + loop: "{{ peers }}" + vars: + domain_name: "{{ item.wg_hub_host_id }}.{{ wg_hub_iface_name }}.local" + ansible.builtin.blockinfile: + path: "/etc/wireguard/{{ wg_hub_iface_name }}.conf" + owner: root + group: root + mode: "0600" + marker: "# {mark} ANSIBLE MANAGED SPOKE BLOCK: {{ domain_name }}" + block: | + # + [Peer] # {{ domain_name }} + PublicKey = {{ item.wg_hub_wg_pubkey }} + PresharedKey = {{ item.wg_psk }} + AllowedIPs = {{ item.wg_hub_ipv4_vpn_addr }}/32 + # diff --git a/roles/wg_hub/vars/main.yml b/roles/wg_hub/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/wg_hub/vars/main.yml @@ -0,0 +1 @@ +--- From 6debe6fb7f530cfeafbf3451f345566aab454ecb Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:13:29 +0300 Subject: [PATCH 10/96] add wg_hub link to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eecffb1..241639d 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,4 @@ - [mount_device](roles/mount_device/README.md) - [alertmanager](roles/alertmanager/README.md) +- [wg_hub](roles/wg_hub/README.md) \ No newline at end of file From 36fa1793247d89e726b9498cfc8377341723606d Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:13:40 +0300 Subject: [PATCH 11/96] update minor version --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index b17cb19..beac011 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.0.1 +version: 0.3.1 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From d8118cdd8c47357c493dfe1b07ee83776a7d5853 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:27:01 +0300 Subject: [PATCH 12/96] add dnsmasq role --- .ansible-lint | 1 + roles/dnsmasq/.ansible-lint | 16 +++++++++ roles/dnsmasq/.gitignore | 2 ++ roles/dnsmasq/.yamllint | 8 +++++ roles/dnsmasq/README.md | 37 ++++++++++++++++++++ roles/dnsmasq/conda.dev.yml | 11 ++++++ roles/dnsmasq/conda.prod.yml | 9 +++++ roles/dnsmasq/defaults/main.yml | 3 ++ roles/dnsmasq/handlers/main.yml | 7 ++++ roles/dnsmasq/meta/main.yml | 17 +++++++++ roles/dnsmasq/molecule/default/converge.yml | 21 ++++++++++++ roles/dnsmasq/molecule/default/molecule.yml | 27 +++++++++++++++ roles/dnsmasq/molecule/default/verify.yml | 19 +++++++++++ roles/dnsmasq/renovate.json | 6 ++++ roles/dnsmasq/requirements.ci.txt | 6 ++++ roles/dnsmasq/requirements.txt | 1 + roles/dnsmasq/requirements.yml | 5 +++ roles/dnsmasq/tasks/main.yml | 38 +++++++++++++++++++++ roles/dnsmasq/vars/main.yml | 1 + 19 files changed, 235 insertions(+) create mode 100644 roles/dnsmasq/.ansible-lint create mode 100644 roles/dnsmasq/.gitignore create mode 100644 roles/dnsmasq/.yamllint create mode 100644 roles/dnsmasq/README.md create mode 100644 roles/dnsmasq/conda.dev.yml create mode 100644 roles/dnsmasq/conda.prod.yml create mode 100644 roles/dnsmasq/defaults/main.yml create mode 100644 roles/dnsmasq/handlers/main.yml create mode 100644 roles/dnsmasq/meta/main.yml create mode 100644 roles/dnsmasq/molecule/default/converge.yml create mode 100644 roles/dnsmasq/molecule/default/molecule.yml create mode 100644 roles/dnsmasq/molecule/default/verify.yml create mode 100644 roles/dnsmasq/renovate.json create mode 100644 roles/dnsmasq/requirements.ci.txt create mode 100644 roles/dnsmasq/requirements.txt create mode 100644 roles/dnsmasq/requirements.yml create mode 100644 roles/dnsmasq/tasks/main.yml create mode 100644 roles/dnsmasq/vars/main.yml diff --git a/.ansible-lint b/.ansible-lint index 43cae14..38f5606 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -7,3 +7,4 @@ loop_var_prefix: "^(__|{role}_)" exclude_paths: - .github/ + - .ansible/ diff --git a/roles/dnsmasq/.ansible-lint b/roles/dnsmasq/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/dnsmasq/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/dnsmasq/.gitignore b/roles/dnsmasq/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/dnsmasq/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/dnsmasq/.yamllint b/roles/dnsmasq/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/dnsmasq/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/dnsmasq/README.md b/roles/dnsmasq/README.md new file mode 100644 index 0000000..2e1d6b4 --- /dev/null +++ b/roles/dnsmasq/README.md @@ -0,0 +1,37 @@ +ansible-dnsmasq +========= + +Deploy dnsmasq on the target node. For now, supports only DNS functionality (DHCP and TFTP are not configuratble). + +Requirements +------------ + +Take care to open the port you choose for dnsmasq to serve queries on. This role does not handle firewall configuration. + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.dnsmasq +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/dnsmasq/conda.dev.yml b/roles/dnsmasq/conda.dev.yml new file mode 100644 index 0000000..d8f50de --- /dev/null +++ b/roles/dnsmasq/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-dnsmasq +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/dnsmasq/conda.prod.yml b/roles/dnsmasq/conda.prod.yml new file mode 100644 index 0000000..51dcb83 --- /dev/null +++ b/roles/dnsmasq/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-dnsmasq +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/dnsmasq/defaults/main.yml b/roles/dnsmasq/defaults/main.yml new file mode 100644 index 0000000..0788c90 --- /dev/null +++ b/roles/dnsmasq/defaults/main.yml @@ -0,0 +1,3 @@ +--- +cache_size: 100 +dns_port: 5300 diff --git a/roles/dnsmasq/handlers/main.yml b/roles/dnsmasq/handlers/main.yml new file mode 100644 index 0000000..c30be1c --- /dev/null +++ b/roles/dnsmasq/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "Restart dnsmasq" + ansible.builtin.systemd_service: + name: dnsmasq + state: restarted + daemon_reload: true + enabled: true diff --git a/roles/dnsmasq/meta/main.yml b/roles/dnsmasq/meta/main.yml new file mode 100644 index 0000000..414d68d --- /dev/null +++ b/roles/dnsmasq/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: dnsmasq + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/dnsmasq/molecule/default/converge.yml b/roles/dnsmasq/molecule/default/converge.yml new file mode 100644 index 0000000..f41738d --- /dev/null +++ b/roles/dnsmasq/molecule/default/converge.yml @@ -0,0 +1,21 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.ufw + disable_ipv6: true + rules: + - rule: allow + # proto: udp + port: 5300 + interface: lo + direction: in + comment: "Allow dnsmasq to serve DNS queries on the given interface" + + - role: genlab.dnsmasq + iface: lo + domain: adm.local + dns_port: 5300 + nodes: + - name: hub + ip: 127.0.0.1 diff --git a/roles/dnsmasq/molecule/default/molecule.yml b/roles/dnsmasq/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/dnsmasq/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/dnsmasq/molecule/default/verify.yml b/roles/dnsmasq/molecule/default/verify.yml new file mode 100644 index 0000000..3a4ca5f --- /dev/null +++ b/roles/dnsmasq/molecule/default/verify.yml @@ -0,0 +1,19 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + pre_tasks: + - name: "Install a package providing the `dig` tool" + ansible.builtin.apt: + name: dnsutils + state: present + + tasks: + - name: "Test the output of the `dig` command" + changed_when: false + register: dig + failed_when: 'dig.stdout != "127.0.0.1"' + ansible.builtin.command: + cmd: "dig @127.0.0.1 -p 5353 hub.adm.vpn +short" diff --git a/roles/dnsmasq/renovate.json b/roles/dnsmasq/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/dnsmasq/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/dnsmasq/requirements.ci.txt b/roles/dnsmasq/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/dnsmasq/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/dnsmasq/requirements.txt b/roles/dnsmasq/requirements.txt new file mode 100644 index 0000000..cd252b9 --- /dev/null +++ b/roles/dnsmasq/requirements.txt @@ -0,0 +1 @@ +ansible~=11.1.0 diff --git a/roles/dnsmasq/requirements.yml b/roles/dnsmasq/requirements.yml new file mode 100644 index 0000000..cf52e39 --- /dev/null +++ b/roles/dnsmasq/requirements.yml @@ -0,0 +1,5 @@ +# requirements file +--- +roles: + - name: genlab.ufw + src: https://github.com/corvus-migratorius/ansible-ufw.git diff --git a/roles/dnsmasq/tasks/main.yml b/roles/dnsmasq/tasks/main.yml new file mode 100644 index 0000000..6f7d0ec --- /dev/null +++ b/roles/dnsmasq/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: "Install dnsmasq" + ansible.builtin.apt: + name: dnsmasq + state: present + update_cache: true + + +- name: "Create interface-specific configuration file" + notify: "Restart dnsmasq" + ansible.builtin.blockinfile: + path: /etc/dnsmasq.d/{{ domain }}.conf + create: true + owner: root + group: root + mode: "0660" + block: | + interface="{{ iface }}" + port="{{ dns_port }}" + cache-size="{{ cache_size }}" + log-queries + server=1.1.1.1 # Cloudflare DNS + server=8.8.8.8 # Google DNS + server=8.8.4.4 # Google DNS (secondary) + + +- name: "Add nodes to the configuration file" + notify: "Restart dnsmasq" + loop: "{{ nodes }}" + ansible.builtin.blockinfile: + path: /etc/dnsmasq.d/{{ domain }}.conf + marker: "# {mark} ANSIBLE MANAGED: {{ item.name }}.{{ domain }}" + block: | + address=/{{ item.name }}.{{ domain }}/{{ item.ip }} + + +- name: "Flush handlers" + ansible.builtin.meta: flush_handlers diff --git a/roles/dnsmasq/vars/main.yml b/roles/dnsmasq/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/dnsmasq/vars/main.yml @@ -0,0 +1 @@ +--- From 0b9ba8964fd3c711fb6dc34ce564bbb2b200ecf2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:28:32 +0300 Subject: [PATCH 13/96] add ufw role --- roles/ufw/.ansible-lint | 17 ++++++++ roles/ufw/.gitignore | 2 + roles/ufw/.yamllint | 8 ++++ roles/ufw/README.md | 37 +++++++++++++++++ roles/ufw/defaults/main.yml | 4 ++ roles/ufw/environment.ci.yml | 10 +++++ roles/ufw/environment.yml | 9 +++++ roles/ufw/handlers/main.yml | 4 ++ roles/ufw/meta/main.yml | 17 ++++++++ roles/ufw/molecule/default/converge.yml | 17 ++++++++ roles/ufw/molecule/default/molecule.yml | 27 +++++++++++++ roles/ufw/molecule/default/verify.yml | 28 +++++++++++++ roles/ufw/renovate.json | 6 +++ roles/ufw/requirements.ci.txt | 6 +++ roles/ufw/requirements.txt | 1 + roles/ufw/tasks/main.yml | 54 +++++++++++++++++++++++++ roles/ufw/vars/main.yml | 1 + 17 files changed, 248 insertions(+) create mode 100644 roles/ufw/.ansible-lint create mode 100644 roles/ufw/.gitignore create mode 100644 roles/ufw/.yamllint create mode 100644 roles/ufw/README.md create mode 100644 roles/ufw/defaults/main.yml create mode 100644 roles/ufw/environment.ci.yml create mode 100644 roles/ufw/environment.yml create mode 100644 roles/ufw/handlers/main.yml create mode 100644 roles/ufw/meta/main.yml create mode 100644 roles/ufw/molecule/default/converge.yml create mode 100644 roles/ufw/molecule/default/molecule.yml create mode 100644 roles/ufw/molecule/default/verify.yml create mode 100644 roles/ufw/renovate.json create mode 100644 roles/ufw/requirements.ci.txt create mode 100644 roles/ufw/requirements.txt create mode 100644 roles/ufw/tasks/main.yml create mode 100644 roles/ufw/vars/main.yml diff --git a/roles/ufw/.ansible-lint b/roles/ufw/.ansible-lint new file mode 100644 index 0000000..64a836a --- /dev/null +++ b/roles/ufw/.ansible-lint @@ -0,0 +1,17 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ + - ufw-rules.yml diff --git a/roles/ufw/.gitignore b/roles/ufw/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/ufw/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/ufw/.yamllint b/roles/ufw/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/ufw/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/ufw/README.md b/roles/ufw/README.md new file mode 100644 index 0000000..7207f41 --- /dev/null +++ b/roles/ufw/README.md @@ -0,0 +1,37 @@ +template +========= + +Whitelist network ports with UFW + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.ufw +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/ufw/defaults/main.yml b/roles/ufw/defaults/main.yml new file mode 100644 index 0000000..7aa973f --- /dev/null +++ b/roles/ufw/defaults/main.yml @@ -0,0 +1,4 @@ +--- +rules: [] +limit_ssh: false +openssh_port: 22 diff --git a/roles/ufw/environment.ci.yml b/roles/ufw/environment.ci.yml new file mode 100644 index 0000000..b91f882 --- /dev/null +++ b/roles/ufw/environment.ci.yml @@ -0,0 +1,10 @@ +--- +name: ansible-ufw +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=22.2 + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/ufw/environment.yml b/roles/ufw/environment.yml new file mode 100644 index 0000000..a5a6851 --- /dev/null +++ b/roles/ufw/environment.yml @@ -0,0 +1,9 @@ +--- +name: ansible-ufw +channels: + - conda-forge +dependencies: + - python~=3.12 + - pip>=22.2 + - pip: + - -r requirements.txt diff --git a/roles/ufw/handlers/main.yml b/roles/ufw/handlers/main.yml new file mode 100644 index 0000000..802d7ec --- /dev/null +++ b/roles/ufw/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: "Reload-ufw" + community.general.ufw: + state: reloaded diff --git a/roles/ufw/meta/main.yml b/roles/ufw/meta/main.yml new file mode 100644 index 0000000..39c7f50 --- /dev/null +++ b/roles/ufw/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: ufw + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Whitelist network ports with UFW" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/ufw/molecule/default/converge.yml b/roles/ufw/molecule/default/converge.yml new file mode 100644 index 0000000..26ef0bd --- /dev/null +++ b/roles/ufw/molecule/default/converge.yml @@ -0,0 +1,17 @@ +--- +- name: Converge + hosts: all + vars: + custom_rules: + - port: 80 + - port: 9080 + src: "10.2.1.0/24" + - interface: eth0@if288 + direction: in + comment: "Allow all incoming traffic on eth0@if288" + + roles: + - role: genlab.ufw + disable_ipv6: true + limit_ssh: true + rules: "{{ custom_rules }}" diff --git a/roles/ufw/molecule/default/molecule.yml b/roles/ufw/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/ufw/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/ufw/molecule/default/verify.yml b/roles/ufw/molecule/default/verify.yml new file mode 100644 index 0000000..3130f87 --- /dev/null +++ b/roles/ufw/molecule/default/verify.yml @@ -0,0 +1,28 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: "Get the UFW status" + register: ufw_status + changed_when: false + ansible.builtin.command: + cmd: ufw status + + - name: "Verify expected UFW status" + vars: + expected: + - "Status: active" + - "" + - "To Action From" + - "-- ------ ----" + - "22/tcp LIMIT Anywhere " + - "80 ALLOW Anywhere " + - "9080 ALLOW 10.2.1.0/24 " + - "Anywhere on eth0@if288 ALLOW Anywhere # Allow all incoming traffic on eth0@if288" + ansible.builtin.assert: + that: ufw_status.stdout_lines == expected + success_msg: "UFW has the expected state" + fail_msg: "Unexpected UFW state (some rules may have not been applied correctly)" diff --git a/roles/ufw/renovate.json b/roles/ufw/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/ufw/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/ufw/requirements.ci.txt b/roles/ufw/requirements.ci.txt new file mode 100644 index 0000000..2316ec9 --- /dev/null +++ b/roles/ufw/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule~=24.12.0 +molecule-plugins[docker] +docker==7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/ufw/requirements.txt b/roles/ufw/requirements.txt new file mode 100644 index 0000000..90d4055 --- /dev/null +++ b/roles/ufw/requirements.txt @@ -0,0 +1 @@ +ansible diff --git a/roles/ufw/tasks/main.yml b/roles/ufw/tasks/main.yml new file mode 100644 index 0000000..21973d5 --- /dev/null +++ b/roles/ufw/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: "Ensure that ufw is installed" + ansible.builtin.apt: + name: ufw + update_cache: true + +- name: "Disable IPv6" + when: (disable_ipv6 is defined) and (disable_ipv6 is true) + ansible.builtin.lineinfile: + path: /etc/default/ufw + regexp: ^IPV6 + line: IPV6=no + +- name: "Deny incoming connections" + notify: Reload-ufw + community.general.ufw: + direction: incoming + proto: any + policy: deny + +- name: "Allow outgoing connections" + notify: Reload-ufw + community.general.ufw: + direction: outgoing + proto: any + policy: allow + +- name: "Allow SSH access" + notify: Reload-ufw + community.general.ufw: + rule: "{{ limit_ssh | ternary('limit', 'allow') }}" + port: "{{ openssh_port }}" + proto: tcp + +- name: "Set whitelist rules" + notify: Reload-ufw + loop: "{{ rules }}" + community.general.ufw: + rule: "{{ item.rule | default('allow') }}" + comment: "{{ item.comment | default(omit) }}" + port: "{{ item.port | default(omit) }}" + proto: "{{ item.proto | default('any') }}" + src: "{{ item.src | default('any') }}" + dest: "{{ item.dest | default(omit) }}" + interface: "{{ item.interface | default(omit) }}" + direction: "{{ item.direction | default(omit) }}" + route: "{{ item.route | default(false) }}" + +- name: "Enable the ufw service" + community.general.ufw: + state: enabled + +- name: "Flush handlers" + ansible.builtin.meta: flush_handlers diff --git a/roles/ufw/vars/main.yml b/roles/ufw/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/ufw/vars/main.yml @@ -0,0 +1 @@ +--- From 20f56a3d194528b209fbb32112ebed8df731642a Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:37:39 +0300 Subject: [PATCH 14/96] fix linter error --- roles/ufw/defaults/main.yml | 6 +++--- roles/ufw/meta/main.yml | 4 ++-- roles/ufw/molecule/default/converge.yml | 6 +++--- roles/ufw/tasks/main.yml | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/roles/ufw/defaults/main.yml b/roles/ufw/defaults/main.yml index 7aa973f..702f638 100644 --- a/roles/ufw/defaults/main.yml +++ b/roles/ufw/defaults/main.yml @@ -1,4 +1,4 @@ --- -rules: [] -limit_ssh: false -openssh_port: 22 +ufw_rules: [] +ufw_limit_ssh: false +ufw_openssh_port: 22 diff --git a/roles/ufw/meta/main.yml b/roles/ufw/meta/main.yml index 39c7f50..004334a 100644 --- a/roles/ufw/meta/main.yml +++ b/roles/ufw/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy" ] + versions: ["focal", "jammy"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/ufw/molecule/default/converge.yml b/roles/ufw/molecule/default/converge.yml index 26ef0bd..ddd580c 100644 --- a/roles/ufw/molecule/default/converge.yml +++ b/roles/ufw/molecule/default/converge.yml @@ -11,7 +11,7 @@ comment: "Allow all incoming traffic on eth0@if288" roles: - - role: genlab.ufw + - role: genlab.common.ufw disable_ipv6: true - limit_ssh: true - rules: "{{ custom_rules }}" + ufw_limit_ssh: true + ufw_rules: "{{ custom_rules }}" diff --git a/roles/ufw/tasks/main.yml b/roles/ufw/tasks/main.yml index 21973d5..30885d3 100644 --- a/roles/ufw/tasks/main.yml +++ b/roles/ufw/tasks/main.yml @@ -28,13 +28,13 @@ - name: "Allow SSH access" notify: Reload-ufw community.general.ufw: - rule: "{{ limit_ssh | ternary('limit', 'allow') }}" - port: "{{ openssh_port }}" + rule: "{{ ufw_limit_ssh | ternary('limit', 'allow') }}" + port: "{{ ufw_openssh_port }}" proto: tcp - name: "Set whitelist rules" notify: Reload-ufw - loop: "{{ rules }}" + loop: "{{ ufw_rules }}" community.general.ufw: rule: "{{ item.rule | default('allow') }}" comment: "{{ item.comment | default(omit) }}" From 233987878e509005c68794429231ddc0e2fb474d Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:38:15 +0300 Subject: [PATCH 15/96] update galaxy version --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index beac011..1c0944f 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.3.1 +version: 0.4.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 0f9fdc9e93852d73e01248451684571d5c92c833 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:40:10 +0300 Subject: [PATCH 16/96] remove excessive files --- roles/ufw/.ansible-lint | 17 ----------------- roles/ufw/.gitignore | 2 -- roles/ufw/.yamllint | 8 -------- roles/ufw/environment.ci.yml | 10 ---------- roles/ufw/environment.yml | 9 --------- roles/ufw/renovate.json | 6 ------ roles/ufw/requirements.ci.txt | 6 ------ roles/ufw/requirements.txt | 1 - 8 files changed, 59 deletions(-) delete mode 100644 roles/ufw/.ansible-lint delete mode 100644 roles/ufw/.gitignore delete mode 100644 roles/ufw/.yamllint delete mode 100644 roles/ufw/environment.ci.yml delete mode 100644 roles/ufw/environment.yml delete mode 100644 roles/ufw/renovate.json delete mode 100644 roles/ufw/requirements.ci.txt delete mode 100644 roles/ufw/requirements.txt diff --git a/roles/ufw/.ansible-lint b/roles/ufw/.ansible-lint deleted file mode 100644 index 64a836a..0000000 --- a/roles/ufw/.ansible-lint +++ /dev/null @@ -1,17 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ - - ufw-rules.yml diff --git a/roles/ufw/.gitignore b/roles/ufw/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/ufw/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/ufw/.yamllint b/roles/ufw/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/ufw/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/ufw/environment.ci.yml b/roles/ufw/environment.ci.yml deleted file mode 100644 index b91f882..0000000 --- a/roles/ufw/environment.ci.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: ansible-ufw -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=22.2 - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/ufw/environment.yml b/roles/ufw/environment.yml deleted file mode 100644 index a5a6851..0000000 --- a/roles/ufw/environment.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-ufw -channels: - - conda-forge -dependencies: - - python~=3.12 - - pip>=22.2 - - pip: - - -r requirements.txt diff --git a/roles/ufw/renovate.json b/roles/ufw/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/ufw/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/ufw/requirements.ci.txt b/roles/ufw/requirements.ci.txt deleted file mode 100644 index 2316ec9..0000000 --- a/roles/ufw/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule~=24.12.0 -molecule-plugins[docker] -docker==7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/ufw/requirements.txt b/roles/ufw/requirements.txt deleted file mode 100644 index 90d4055..0000000 --- a/roles/ufw/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible From bfdb8cad552fb0e49e7e34bf655496abea44e11f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 11 Dec 2025 10:49:01 +0300 Subject: [PATCH 17/96] add community.general installation in github actions --- .github/workflows/linters.yml | 4 ++++ .github/workflows/molecule.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index b90a6ea..0aace81 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -26,6 +26,10 @@ jobs: # run: ansible-galaxy install -r requirements.yml # shell: micromamba-shell {0} + - name: "Install community.general collection" + run: ansible-galaxy collection install community.general + shell: micromamba-shell {0} + - name: Check workflow files themselves with ActionLint run: actionlint shell: micromamba-shell {0} diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml index 2fde508..97885ee 100644 --- a/.github/workflows/molecule.yml +++ b/.github/workflows/molecule.yml @@ -78,6 +78,10 @@ jobs: run: ansible-galaxy install -r requirements.yml shell: micromamba-shell {0} + - name: "Install community.general collection" + run: ansible-galaxy collection install community.general + shell: micromamba-shell {0} + - name: "Run Molecule tests" if: ${{ matrix.role != '__no_role__' }} working-directory: ${{ matrix.role }} From c1311a77c4a4d08e66bbae55485ba634711b779e Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 10:32:18 +0300 Subject: [PATCH 18/96] fix linter errors --- roles/dnsmasq/defaults/main.yml | 4 ++-- roles/dnsmasq/meta/main.yml | 4 ++-- roles/dnsmasq/molecule/default/converge.yml | 12 ++++++------ roles/dnsmasq/molecule/default/verify.yml | 2 +- roles/dnsmasq/tasks/main.yml | 18 +++++++++--------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/roles/dnsmasq/defaults/main.yml b/roles/dnsmasq/defaults/main.yml index 0788c90..24e3d56 100644 --- a/roles/dnsmasq/defaults/main.yml +++ b/roles/dnsmasq/defaults/main.yml @@ -1,3 +1,3 @@ --- -cache_size: 100 -dns_port: 5300 +dnsmasq_cache_size: 100 +dnsmasq_dns_port: 5300 diff --git a/roles/dnsmasq/meta/main.yml b/roles/dnsmasq/meta/main.yml index 414d68d..03ddef7 100644 --- a/roles/dnsmasq/meta/main.yml +++ b/roles/dnsmasq/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy" ] + versions: ["focal", "jammy"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/dnsmasq/molecule/default/converge.yml b/roles/dnsmasq/molecule/default/converge.yml index f41738d..6a8f31d 100644 --- a/roles/dnsmasq/molecule/default/converge.yml +++ b/roles/dnsmasq/molecule/default/converge.yml @@ -2,7 +2,7 @@ - name: Converge hosts: all roles: - - role: genlab.ufw + - role: genlab.common.ufw disable_ipv6: true rules: - rule: allow @@ -12,10 +12,10 @@ direction: in comment: "Allow dnsmasq to serve DNS queries on the given interface" - - role: genlab.dnsmasq - iface: lo - domain: adm.local - dns_port: 5300 - nodes: + - role: genlab.common.dnsmasq + dnsmasq_iface: lo + dnsmasq_domain: adm.local + dnsmasq_dns_port: 5300 + dnsmasq_nodes: - name: hub ip: 127.0.0.1 diff --git a/roles/dnsmasq/molecule/default/verify.yml b/roles/dnsmasq/molecule/default/verify.yml index 3a4ca5f..8eaf15a 100644 --- a/roles/dnsmasq/molecule/default/verify.yml +++ b/roles/dnsmasq/molecule/default/verify.yml @@ -13,7 +13,7 @@ tasks: - name: "Test the output of the `dig` command" changed_when: false - register: dig + register: dnsmasq_dig failed_when: 'dig.stdout != "127.0.0.1"' ansible.builtin.command: cmd: "dig @127.0.0.1 -p 5353 hub.adm.vpn +short" diff --git a/roles/dnsmasq/tasks/main.yml b/roles/dnsmasq/tasks/main.yml index 6f7d0ec..ff29987 100644 --- a/roles/dnsmasq/tasks/main.yml +++ b/roles/dnsmasq/tasks/main.yml @@ -9,29 +9,29 @@ - name: "Create interface-specific configuration file" notify: "Restart dnsmasq" ansible.builtin.blockinfile: - path: /etc/dnsmasq.d/{{ domain }}.conf + path: /etc/dnsmasq.d/{{ dnsmasq_domain }}.conf create: true owner: root group: root mode: "0660" block: | - interface="{{ iface }}" - port="{{ dns_port }}" - cache-size="{{ cache_size }}" + interface="{{ dnsmasq_iface }}" + port="{{ dnsmasq_dns_port }}" + cache-size="{{ dnsmasq_cache_size }}" log-queries server=1.1.1.1 # Cloudflare DNS server=8.8.8.8 # Google DNS server=8.8.4.4 # Google DNS (secondary) -- name: "Add nodes to the configuration file" +- name: "Add dnsmasq_nodes to the configuration file" notify: "Restart dnsmasq" - loop: "{{ nodes }}" + loop: "{{ dnsmasq_nodes }}" ansible.builtin.blockinfile: - path: /etc/dnsmasq.d/{{ domain }}.conf - marker: "# {mark} ANSIBLE MANAGED: {{ item.name }}.{{ domain }}" + path: /etc/dnsmasq.d/{{ dnsmasq_domain }}.conf + marker: "# {mark} ANSIBLE MANAGED: {{ item.name }}.{{ dnsmasq_domain }}" block: | - address=/{{ item.name }}.{{ domain }}/{{ item.ip }} + address=/{{ item.name }}.{{ dnsmasq_domain }}/{{ item.ip }} - name: "Flush handlers" From a1323ea97a709cf37156b59a130870e616d7d2d2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 10:32:30 +0300 Subject: [PATCH 19/96] fix verify task --- roles/dnsmasq/molecule/default/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/dnsmasq/molecule/default/verify.yml b/roles/dnsmasq/molecule/default/verify.yml index 8eaf15a..f9182a5 100644 --- a/roles/dnsmasq/molecule/default/verify.yml +++ b/roles/dnsmasq/molecule/default/verify.yml @@ -16,4 +16,4 @@ register: dnsmasq_dig failed_when: 'dig.stdout != "127.0.0.1"' ansible.builtin.command: - cmd: "dig @127.0.0.1 -p 5353 hub.adm.vpn +short" + cmd: "dig @127.0.0.1 -p 5300 hub.adm.local +short" From 640c26279be9eafffdf9dae4c689a7946837f4b6 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 10:37:51 +0300 Subject: [PATCH 20/96] fix verify error --- roles/dnsmasq/molecule/default/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/dnsmasq/molecule/default/verify.yml b/roles/dnsmasq/molecule/default/verify.yml index f9182a5..9a45b9e 100644 --- a/roles/dnsmasq/molecule/default/verify.yml +++ b/roles/dnsmasq/molecule/default/verify.yml @@ -14,6 +14,6 @@ - name: "Test the output of the `dig` command" changed_when: false register: dnsmasq_dig - failed_when: 'dig.stdout != "127.0.0.1"' + failed_when: 'dnsmasq_dig.stdout != "127.0.0.1"' ansible.builtin.command: cmd: "dig @127.0.0.1 -p 5300 hub.adm.local +short" From 5f8e8eef16ebf532a342f55fd0684d6d6b58aaaa Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 11:27:02 +0300 Subject: [PATCH 21/96] update roles list in README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index dc6784a..af97688 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,8 @@ ## Roles +- [alertmanager](roles/alertmanager/README.md) +- [dnsmasq](roles/dnsmasq/README.md) - [mount_device](roles/mount_device/README.md) +- [ufw](roles/ufw/README.md) +- [wg_hub](roles/wg_hub/README.md) \ No newline at end of file From b5202a3b3e2dc986591ee3c325f42606075ad3e2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 11:27:30 +0300 Subject: [PATCH 22/96] update galaxy version --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index b17cb19..6f50d1d 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.0.1 +version: 0.5.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From a665400ce82bbdb2c42ff0c1c2b26ed4d5e92286 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 11:37:20 +0300 Subject: [PATCH 23/96] remove garbage --- roles/dnsmasq/.ansible-lint | 16 ---------------- roles/dnsmasq/.gitignore | 2 -- roles/dnsmasq/.yamllint | 8 -------- roles/dnsmasq/conda.dev.yml | 11 ----------- roles/dnsmasq/conda.prod.yml | 9 --------- roles/dnsmasq/renovate.json | 6 ------ roles/dnsmasq/requirements.ci.txt | 6 ------ roles/dnsmasq/requirements.txt | 1 - roles/dnsmasq/requirements.yml | 5 ----- 9 files changed, 64 deletions(-) delete mode 100644 roles/dnsmasq/.ansible-lint delete mode 100644 roles/dnsmasq/.gitignore delete mode 100644 roles/dnsmasq/.yamllint delete mode 100644 roles/dnsmasq/conda.dev.yml delete mode 100644 roles/dnsmasq/conda.prod.yml delete mode 100644 roles/dnsmasq/renovate.json delete mode 100644 roles/dnsmasq/requirements.ci.txt delete mode 100644 roles/dnsmasq/requirements.txt delete mode 100644 roles/dnsmasq/requirements.yml diff --git a/roles/dnsmasq/.ansible-lint b/roles/dnsmasq/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/dnsmasq/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/dnsmasq/.gitignore b/roles/dnsmasq/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/dnsmasq/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/dnsmasq/.yamllint b/roles/dnsmasq/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/dnsmasq/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/dnsmasq/conda.dev.yml b/roles/dnsmasq/conda.dev.yml deleted file mode 100644 index d8f50de..0000000 --- a/roles/dnsmasq/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-dnsmasq -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/dnsmasq/conda.prod.yml b/roles/dnsmasq/conda.prod.yml deleted file mode 100644 index 51dcb83..0000000 --- a/roles/dnsmasq/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-dnsmasq -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/dnsmasq/renovate.json b/roles/dnsmasq/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/dnsmasq/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/dnsmasq/requirements.ci.txt b/roles/dnsmasq/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/dnsmasq/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/dnsmasq/requirements.txt b/roles/dnsmasq/requirements.txt deleted file mode 100644 index cd252b9..0000000 --- a/roles/dnsmasq/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.1.0 diff --git a/roles/dnsmasq/requirements.yml b/roles/dnsmasq/requirements.yml deleted file mode 100644 index cf52e39..0000000 --- a/roles/dnsmasq/requirements.yml +++ /dev/null @@ -1,5 +0,0 @@ -# requirements file ---- -roles: - - name: genlab.ufw - src: https://github.com/corvus-migratorius/ansible-ufw.git From cb4f2672cad5d235f53c562ae080c9a5529ac9fc Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 11:44:47 +0300 Subject: [PATCH 24/96] add prometheus role --- roles/prometheus/.ansible-lint | 17 ++++ roles/prometheus/.gitignore | 2 + roles/prometheus/.yamllint | 8 ++ roles/prometheus/README.md | 50 +++++++++++ roles/prometheus/conda.dev.yml | 11 +++ roles/prometheus/conda.prod.yml | 9 ++ roles/prometheus/defaults/main.yml | 7 ++ roles/prometheus/handlers/main.yml | 8 ++ roles/prometheus/meta/main.yml | 17 ++++ .../prometheus/molecule/default/converge.yml | 8 ++ .../prometheus/molecule/default/molecule.yml | 27 ++++++ .../default/prometheus/prometheus.yml | 14 ++++ .../default/prometheus/rules/alertrules.yml | 13 +++ roles/prometheus/molecule/default/verify.yml | 55 ++++++++++++ roles/prometheus/renovate.json | 6 ++ roles/prometheus/requirements.ci.txt | 6 ++ roles/prometheus/requirements.txt | 1 + roles/prometheus/requirements.yml | 3 + roles/prometheus/tasks/configure.yml | 23 +++++ roles/prometheus/tasks/install.yml | 84 +++++++++++++++++++ roles/prometheus/tasks/main.yml | 9 ++ .../templates/prometheus.service.j2 | 28 +++++++ roles/prometheus/vars/main.yml | 1 + 23 files changed, 407 insertions(+) create mode 100644 roles/prometheus/.ansible-lint create mode 100644 roles/prometheus/.gitignore create mode 100644 roles/prometheus/.yamllint create mode 100644 roles/prometheus/README.md create mode 100644 roles/prometheus/conda.dev.yml create mode 100644 roles/prometheus/conda.prod.yml create mode 100644 roles/prometheus/defaults/main.yml create mode 100644 roles/prometheus/handlers/main.yml create mode 100644 roles/prometheus/meta/main.yml create mode 100644 roles/prometheus/molecule/default/converge.yml create mode 100644 roles/prometheus/molecule/default/molecule.yml create mode 100644 roles/prometheus/molecule/default/prometheus/prometheus.yml create mode 100644 roles/prometheus/molecule/default/prometheus/rules/alertrules.yml create mode 100644 roles/prometheus/molecule/default/verify.yml create mode 100644 roles/prometheus/renovate.json create mode 100644 roles/prometheus/requirements.ci.txt create mode 100644 roles/prometheus/requirements.txt create mode 100644 roles/prometheus/requirements.yml create mode 100644 roles/prometheus/tasks/configure.yml create mode 100644 roles/prometheus/tasks/install.yml create mode 100644 roles/prometheus/tasks/main.yml create mode 100644 roles/prometheus/templates/prometheus.service.j2 create mode 100644 roles/prometheus/vars/main.yml diff --git a/roles/prometheus/.ansible-lint b/roles/prometheus/.ansible-lint new file mode 100644 index 0000000..3a5bc7d --- /dev/null +++ b/roles/prometheus/.ansible-lint @@ -0,0 +1,17 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ + - molecule/default/prometheus/ diff --git a/roles/prometheus/.gitignore b/roles/prometheus/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/prometheus/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/prometheus/.yamllint b/roles/prometheus/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/prometheus/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/prometheus/README.md b/roles/prometheus/README.md new file mode 100644 index 0000000..d8894ce --- /dev/null +++ b/roles/prometheus/README.md @@ -0,0 +1,50 @@ +genlab.prometheus +========= + +This ansible role installs [Prometheus](https://github.com/prometheus/prometheus) from binary distribution. Prometheus is a systems and service monitoring system. It collects metrics from configured targets at given intervals, evaluates rule expressions, displays the results, and can trigger alerts when specified conditions are observed. + +Prometheus configuration files and rules should be placed in separate directories in the source. They must have a `*.yml` suffix. You'll need to manually specify the paths to your rule files in the main Prometheus configuration file (prometheus.yml) on the target machine + +Requirements +------------ + +None + +Role Variables +-------------- +``` +prometheus_version: 3.2.1 # prometheus version +prometheus_dir: "/etc/prometheus" # where to install prometheus +prometheus_user: prometheus # user name +prometheus_group: prometheus # groups name +config_dir: "/etc/prometheus/conf" # where to place prometheus config +db_dir: "/var/lib/prometheus" # where to store prometheus db +config_source_dir: prometheus # path to config files on source +alertrules_source_dir: prometheus/rules # path to rule files on source +``` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.prometheus + config_source_dir: mydir/configs + alertrules_source_dir: mydir/configs/rules + prometheus_version: 3.2.1 +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/prometheus/conda.dev.yml b/roles/prometheus/conda.dev.yml new file mode 100644 index 0000000..fb974b7 --- /dev/null +++ b/roles/prometheus/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/prometheus/conda.prod.yml b/roles/prometheus/conda.prod.yml new file mode 100644 index 0000000..d6d12d6 --- /dev/null +++ b/roles/prometheus/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/prometheus/defaults/main.yml b/roles/prometheus/defaults/main.yml new file mode 100644 index 0000000..891c50d --- /dev/null +++ b/roles/prometheus/defaults/main.yml @@ -0,0 +1,7 @@ +--- +prometheus_version: 3.2.1 +prometheus_dir: "/etc/prometheus" +prometheus_user: prometheus +prometheus_group: prometheus +config_dir: "/etc/prometheus/conf" +db_dir: "/var/lib/prometheus" diff --git a/roles/prometheus/handlers/main.yml b/roles/prometheus/handlers/main.yml new file mode 100644 index 0000000..29ac016 --- /dev/null +++ b/roles/prometheus/handlers/main.yml @@ -0,0 +1,8 @@ +--- +- name: "(Re)start Prometheus service" + become: true + ansible.builtin.systemd: + name: prometheus.service + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/prometheus/meta/main.yml b/roles/prometheus/meta/main.yml new file mode 100644 index 0000000..b65335e --- /dev/null +++ b/roles/prometheus/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "prometheus" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/prometheus/molecule/default/converge.yml b/roles/prometheus/molecule/default/converge.yml new file mode 100644 index 0000000..1d9f0cf --- /dev/null +++ b/roles/prometheus/molecule/default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + + roles: + - role: genlab.prometheus + config_source_dir: prometheus + alertrules_source_dir: prometheus/rules diff --git a/roles/prometheus/molecule/default/molecule.yml b/roles/prometheus/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/prometheus/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/prometheus/molecule/default/prometheus/prometheus.yml b/roles/prometheus/molecule/default/prometheus/prometheus.yml new file mode 100644 index 0000000..9368ee3 --- /dev/null +++ b/roles/prometheus/molecule/default/prometheus/prometheus.yml @@ -0,0 +1,14 @@ +--- +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + - /etc/prometheus/conf/alertules.yml + # - "first.rules" + # - "second.rules" + +scrape_configs: + - job_name: prometheus + static_configs: + - targets: ['localhost:9090'] \ No newline at end of file diff --git a/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml b/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml new file mode 100644 index 0000000..f85947d --- /dev/null +++ b/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml @@ -0,0 +1,13 @@ +groups: +- name: example + labels: + team: myteam + rules: + - alert: HighRequestLatency + expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 + for: 10m + keep_firing_for: 5m + labels: + severity: page + annotations: + summary: High request latency \ No newline at end of file diff --git a/roles/prometheus/molecule/default/verify.yml b/roles/prometheus/molecule/default/verify.yml new file mode 100644 index 0000000..350252a --- /dev/null +++ b/roles/prometheus/molecule/default/verify.yml @@ -0,0 +1,55 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Check if Prometheus is installed" + changed_when: false + ansible.builtin.command: "prometheus --version" + register: prom_installed_version + + - name: "Check Prometheus version" + ansible.builtin.assert: + that: "prom_installed_version.stdout is regex('{{ prometheus_version }}')" + success_msg: "Prometheus version {{ prometheus_version }} is installed and working" + fail_msg: "Prometheus version {{ prometheus_version }} is not installed or not working correctly" + + # kics-scan ignore-block + - name: "Check if Prometheus is reachable" + ansible.builtin.uri: + url: "http://localhost:9090/-/healthy" + return_content: true + status_code: 200 + method: GET + body_format: json + register: prom_health + + - name: "Debug Prometheus health status" + ansible.builtin.assert: + that: "prom_health.content | trim == 'Prometheus Server is Healthy.'" + success_msg: "Prometheus is healthy" + fail_msg: "Prometheus is not healthy" + + # kics-scan ignore-block + - name: "Check if Prometheus is ready" + ansible.builtin.uri: + url: "http://localhost:9090/-/ready" + return_content: true + status_code: 200 + method: GET + body_format: json + register: prom_ready + + - name: "Debug Prometheus readiness status" + ansible.builtin.assert: + that: "prom_ready.content | trim == 'Prometheus Server is Ready.'" + success_msg: "Prometheus is ready" + fail_msg: "Prometheus is not ready" diff --git a/roles/prometheus/renovate.json b/roles/prometheus/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/prometheus/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/prometheus/requirements.ci.txt b/roles/prometheus/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/prometheus/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/prometheus/requirements.txt b/roles/prometheus/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/prometheus/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/prometheus/requirements.yml b/roles/prometheus/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/prometheus/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/prometheus/tasks/configure.yml b/roles/prometheus/tasks/configure.yml new file mode 100644 index 0000000..8e31514 --- /dev/null +++ b/roles/prometheus/tasks/configure.yml @@ -0,0 +1,23 @@ +--- +- name: "Copy config file" + notify: "(Re)start Prometheus service" + ansible.builtin.copy: + src: "{{ item }}" + dest: "{{ config_dir }}/{{ item | basename }}" + owner: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + mode: "0660" + with_fileglob: + - "{{ config_source_dir }}/*.yml" + +- name: "Copy alert rules" + notify: "(Re)start Prometheus service" + when: alertrules_source_dir is defined + ansible.builtin.copy: + src: "{{ item }}" + dest: "{{ config_dir }}/{{ item | basename }}" + owner: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + mode: "0660" + with_fileglob: + - "{{ alertrules_source_dir }}/*.yml" diff --git a/roles/prometheus/tasks/install.yml b/roles/prometheus/tasks/install.yml new file mode 100644 index 0000000..05ebb7a --- /dev/null +++ b/roles/prometheus/tasks/install.yml @@ -0,0 +1,84 @@ +--- +- name: "Create Prometheus system group" + ansible.builtin.group: + name: "{{ prometheus_group }}" + system: true + state: present + +- name: "Create Prometheus system user" + ansible.builtin.user: + name: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + system: true + shell: "/sbin/nologin" + create_home: false + state: present + +- name: "Install prometheus from binary" + block: + - name: "Check Prometheus version" + changed_when: false + ansible.builtin.command: + cmd: "prometheus --version" + register: prometheus_ver + + - name: "Assert version correctness" + ansible.builtin.assert: + that: "prometheus_ver.stdout is regex('{{ prometheus_version }}')" + success_msg: "prometheus version {{ prometheus_version }} is installed and working" + fail_msg: "prometheus version {{ prometheus_version }} is not installed or not working correctly" + + rescue: + - name: "Create prometheus directories {{ item }}" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + mode: "0755" + with_items: + - "{{ config_dir }}" + - "{{ prometheus_dir }}" + - "{{ db_dir }}" + + - name: "Download Prometheus binary" + ansible.builtin.get_url: + url: "https://github.com/prometheus/prometheus/releases/download/v{{ prometheus_version }}/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz" + dest: "/tmp/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz" + owner: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + mode: "0644" + + - name: "Unpack Prometheus binaries" + notify: "(Re)start Prometheus service" + ansible.builtin.unarchive: + src: "/tmp/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz" + dest: "{{ prometheus_dir }}" + creates: "{{ prometheus_dir }}/prometheus-{{ prometheus_version }}.linux-amd64" + remote_src: true + + - name: "Cleanup downloaded file" + ansible.builtin.file: + path: "/tmp/prometheus-{{ prometheus_version }}.linux-amd64.tar.gz" + state: absent + + - name: "Move official prometheus and promtool binaries" + ansible.builtin.copy: + src: "{{ prometheus_dir }}/prometheus-{{ prometheus_version }}.linux-amd64/{{ item }}" + dest: "/usr/local/bin/{{ item }}" + mode: "0755" + owner: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + remote_src: true + with_items: + - prometheus + - promtool + +- name: "Create systemd service unit" + notify: "(Re)start Prometheus service" + ansible.builtin.template: + src: prometheus.service.j2 + dest: /etc/systemd/system/prometheus.service + owner: "{{ prometheus_user }}" + group: "{{ prometheus_group }}" + mode: "0660" diff --git a/roles/prometheus/tasks/main.yml b/roles/prometheus/tasks/main.yml new file mode 100644 index 0000000..43bcfba --- /dev/null +++ b/roles/prometheus/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: "Install Prometheus from binary" + ansible.builtin.include_tasks: install.yml + +- name: "Configure and add alert rules" + ansible.builtin.include_tasks: configure.yml + +- name: "Flush handlers" + ansible.builtin.meta: "flush_handlers" diff --git a/roles/prometheus/templates/prometheus.service.j2 b/roles/prometheus/templates/prometheus.service.j2 new file mode 100644 index 0000000..839c5f4 --- /dev/null +++ b/roles/prometheus/templates/prometheus.service.j2 @@ -0,0 +1,28 @@ +[Unit] +Description=Prometheus +Wants=network-online.target +After=network-online.target +Documentation="https://prometheus.io/" + +[Service] +User=prometheus +Group=prometheus +Type=simple +ExecStart=/usr/local/bin/prometheus \ + --config.file {{ config_dir }}/prometheus.yml \ + --storage.tsdb.path {{ db_dir }} \ + --storage.tsdb.retention.time=30d + +# Security hardening +ReadWritePaths={{ db_dir }} +ProtectSystem=strict +NoNewPrivileges=true +PrivateTmp=true +ProtectKernelModules=true +ProtectControlGroups=true +ProtectKernelTunables=true +ProtectClock=yes +RestrictSUIDSGID=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/prometheus/vars/main.yml b/roles/prometheus/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/prometheus/vars/main.yml @@ -0,0 +1 @@ +--- From 1f371faad6ee13880c08cd0becb63d7fbb3ac92b Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 12:05:49 +0300 Subject: [PATCH 25/96] fix linter errors --- roles/prometheus/README.md | 8 +++---- roles/prometheus/defaults/main.yml | 4 ++-- roles/prometheus/meta/main.yml | 4 ++-- .../prometheus/molecule/default/converge.yml | 4 ++-- .../default/prometheus/prometheus.yml | 4 ++-- .../default/prometheus/rules/alertrules.yml | 22 +++++++++---------- roles/prometheus/molecule/default/verify.yml | 14 ++++++------ roles/prometheus/tasks/configure.yml | 6 ++--- roles/prometheus/tasks/install.yml | 4 ++-- .../templates/prometheus.service.j2 | 6 ++--- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/roles/prometheus/README.md b/roles/prometheus/README.md index d8894ce..672f379 100644 --- a/roles/prometheus/README.md +++ b/roles/prometheus/README.md @@ -17,9 +17,9 @@ prometheus_version: 3.2.1 # prometheus version prometheus_dir: "/etc/prometheus" # where to install prometheus prometheus_user: prometheus # user name prometheus_group: prometheus # groups name -config_dir: "/etc/prometheus/conf" # where to place prometheus config -db_dir: "/var/lib/prometheus" # where to store prometheus db -config_source_dir: prometheus # path to config files on source +prometheus_config_dir: "/etc/prometheus/conf" # where to place prometheus config +prometheus_db_dir: "/var/lib/prometheus" # where to store prometheus db +prometheus_config_source_dir: prometheus # path to config files on source alertrules_source_dir: prometheus/rules # path to rule files on source ``` @@ -34,7 +34,7 @@ Example Playbook ```yaml roles: - role: genlab.prometheus - config_source_dir: mydir/configs + prometheus_config_source_dir: mydir/configs alertrules_source_dir: mydir/configs/rules prometheus_version: 3.2.1 ``` diff --git a/roles/prometheus/defaults/main.yml b/roles/prometheus/defaults/main.yml index 891c50d..5ecc61c 100644 --- a/roles/prometheus/defaults/main.yml +++ b/roles/prometheus/defaults/main.yml @@ -3,5 +3,5 @@ prometheus_version: 3.2.1 prometheus_dir: "/etc/prometheus" prometheus_user: prometheus prometheus_group: prometheus -config_dir: "/etc/prometheus/conf" -db_dir: "/var/lib/prometheus" +prometheus_config_dir: "/etc/prometheus/conf" +prometheus_db_dir: "/var/lib/prometheus" diff --git a/roles/prometheus/meta/main.yml b/roles/prometheus/meta/main.yml index b65335e..b4335ec 100644 --- a/roles/prometheus/meta/main.yml +++ b/roles/prometheus/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/prometheus/molecule/default/converge.yml b/roles/prometheus/molecule/default/converge.yml index 1d9f0cf..a9b0439 100644 --- a/roles/prometheus/molecule/default/converge.yml +++ b/roles/prometheus/molecule/default/converge.yml @@ -3,6 +3,6 @@ hosts: all roles: - - role: genlab.prometheus - config_source_dir: prometheus + - role: genlab.common.prometheus + prometheus_config_source_dir: prometheus alertrules_source_dir: prometheus/rules diff --git a/roles/prometheus/molecule/default/prometheus/prometheus.yml b/roles/prometheus/molecule/default/prometheus/prometheus.yml index 9368ee3..31854f4 100644 --- a/roles/prometheus/molecule/default/prometheus/prometheus.yml +++ b/roles/prometheus/molecule/default/prometheus/prometheus.yml @@ -1,6 +1,6 @@ --- global: - scrape_interval: 15s + scrape_interval: 15s evaluation_interval: 15s rule_files: @@ -11,4 +11,4 @@ rule_files: scrape_configs: - job_name: prometheus static_configs: - - targets: ['localhost:9090'] \ No newline at end of file + - targets: ['localhost:9090'] diff --git a/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml b/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml index f85947d..dc3c47f 100644 --- a/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml +++ b/roles/prometheus/molecule/default/prometheus/rules/alertrules.yml @@ -1,13 +1,13 @@ groups: -- name: example - labels: - team: myteam - rules: - - alert: HighRequestLatency - expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 - for: 10m - keep_firing_for: 5m + - name: example labels: - severity: page - annotations: - summary: High request latency \ No newline at end of file + team: myteam + rules: + - alert: HighRequestLatency + expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 + for: 10m + keep_firing_for: 5m + labels: + severity: page + annotations: + summary: High request latency diff --git a/roles/prometheus/molecule/default/verify.yml b/roles/prometheus/molecule/default/verify.yml index 350252a..c085dd1 100644 --- a/roles/prometheus/molecule/default/verify.yml +++ b/roles/prometheus/molecule/default/verify.yml @@ -9,16 +9,16 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Check if Prometheus is installed" changed_when: false ansible.builtin.command: "prometheus --version" - register: prom_installed_version + register: prometheus_installed_version - name: "Check Prometheus version" ansible.builtin.assert: - that: "prom_installed_version.stdout is regex('{{ prometheus_version }}')" + that: "prometheus_installed_version.stdout is regex('{{ prometheus_version }}')" success_msg: "Prometheus version {{ prometheus_version }} is installed and working" fail_msg: "Prometheus version {{ prometheus_version }} is not installed or not working correctly" @@ -30,11 +30,11 @@ status_code: 200 method: GET body_format: json - register: prom_health + register: prometheus_health - name: "Debug Prometheus health status" ansible.builtin.assert: - that: "prom_health.content | trim == 'Prometheus Server is Healthy.'" + that: "prometheus_health.content | trim == 'Prometheus Server is Healthy.'" success_msg: "Prometheus is healthy" fail_msg: "Prometheus is not healthy" @@ -46,10 +46,10 @@ status_code: 200 method: GET body_format: json - register: prom_ready + register: prometheus_ready - name: "Debug Prometheus readiness status" ansible.builtin.assert: - that: "prom_ready.content | trim == 'Prometheus Server is Ready.'" + that: "prometheus_ready.content | trim == 'Prometheus Server is Ready.'" success_msg: "Prometheus is ready" fail_msg: "Prometheus is not ready" diff --git a/roles/prometheus/tasks/configure.yml b/roles/prometheus/tasks/configure.yml index 8e31514..cbad1ad 100644 --- a/roles/prometheus/tasks/configure.yml +++ b/roles/prometheus/tasks/configure.yml @@ -3,19 +3,19 @@ notify: "(Re)start Prometheus service" ansible.builtin.copy: src: "{{ item }}" - dest: "{{ config_dir }}/{{ item | basename }}" + dest: "{{ prometheus_config_dir }}/{{ item | basename }}" owner: "{{ prometheus_user }}" group: "{{ prometheus_group }}" mode: "0660" with_fileglob: - - "{{ config_source_dir }}/*.yml" + - "{{ prometheus_config_source_dir }}/*.yml" - name: "Copy alert rules" notify: "(Re)start Prometheus service" when: alertrules_source_dir is defined ansible.builtin.copy: src: "{{ item }}" - dest: "{{ config_dir }}/{{ item | basename }}" + dest: "{{ prometheus_config_dir }}/{{ item | basename }}" owner: "{{ prometheus_user }}" group: "{{ prometheus_group }}" mode: "0660" diff --git a/roles/prometheus/tasks/install.yml b/roles/prometheus/tasks/install.yml index 05ebb7a..3118047 100644 --- a/roles/prometheus/tasks/install.yml +++ b/roles/prometheus/tasks/install.yml @@ -37,9 +37,9 @@ group: "{{ prometheus_group }}" mode: "0755" with_items: - - "{{ config_dir }}" + - "{{ prometheus_config_dir }}" - "{{ prometheus_dir }}" - - "{{ db_dir }}" + - "{{ prometheus_db_dir }}" - name: "Download Prometheus binary" ansible.builtin.get_url: diff --git a/roles/prometheus/templates/prometheus.service.j2 b/roles/prometheus/templates/prometheus.service.j2 index 839c5f4..2f17191 100644 --- a/roles/prometheus/templates/prometheus.service.j2 +++ b/roles/prometheus/templates/prometheus.service.j2 @@ -9,12 +9,12 @@ User=prometheus Group=prometheus Type=simple ExecStart=/usr/local/bin/prometheus \ - --config.file {{ config_dir }}/prometheus.yml \ - --storage.tsdb.path {{ db_dir }} \ + --config.file {{ prometheus_config_dir }}/prometheus.yml \ + --storage.tsdb.path {{ prometheus_db_dir }} \ --storage.tsdb.retention.time=30d # Security hardening -ReadWritePaths={{ db_dir }} +ReadWritePaths={{ prometheus_db_dir }} ProtectSystem=strict NoNewPrivileges=true PrivateTmp=true From d5af8efcf7413e7d2ea23bdd85650e21c9e04938 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 12:07:00 +0300 Subject: [PATCH 26/96] remove garbage --- roles/prometheus/.ansible-lint | 17 ----------------- roles/prometheus/.gitignore | 2 -- roles/prometheus/.yamllint | 8 -------- roles/prometheus/conda.dev.yml | 11 ----------- roles/prometheus/conda.prod.yml | 9 --------- roles/prometheus/renovate.json | 6 ------ roles/prometheus/requirements.ci.txt | 6 ------ roles/prometheus/requirements.txt | 1 - roles/prometheus/requirements.yml | 3 --- 9 files changed, 63 deletions(-) delete mode 100644 roles/prometheus/.ansible-lint delete mode 100644 roles/prometheus/.gitignore delete mode 100644 roles/prometheus/.yamllint delete mode 100644 roles/prometheus/conda.dev.yml delete mode 100644 roles/prometheus/conda.prod.yml delete mode 100644 roles/prometheus/renovate.json delete mode 100644 roles/prometheus/requirements.ci.txt delete mode 100644 roles/prometheus/requirements.txt delete mode 100644 roles/prometheus/requirements.yml diff --git a/roles/prometheus/.ansible-lint b/roles/prometheus/.ansible-lint deleted file mode 100644 index 3a5bc7d..0000000 --- a/roles/prometheus/.ansible-lint +++ /dev/null @@ -1,17 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ - - molecule/default/prometheus/ diff --git a/roles/prometheus/.gitignore b/roles/prometheus/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/prometheus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/prometheus/.yamllint b/roles/prometheus/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/prometheus/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/prometheus/conda.dev.yml b/roles/prometheus/conda.dev.yml deleted file mode 100644 index fb974b7..0000000 --- a/roles/prometheus/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/prometheus/conda.prod.yml b/roles/prometheus/conda.prod.yml deleted file mode 100644 index d6d12d6..0000000 --- a/roles/prometheus/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/prometheus/renovate.json b/roles/prometheus/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/prometheus/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/prometheus/requirements.ci.txt b/roles/prometheus/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/prometheus/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/prometheus/requirements.txt b/roles/prometheus/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/prometheus/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 diff --git a/roles/prometheus/requirements.yml b/roles/prometheus/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/prometheus/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From 6464be7fdfe24913216bb930ef96d0dc1502adb4 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 12:09:03 +0300 Subject: [PATCH 27/96] update README.md's --- README.md | 1 + roles/prometheus/README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index af97688..e571129 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,6 @@ - [alertmanager](roles/alertmanager/README.md) - [dnsmasq](roles/dnsmasq/README.md) - [mount_device](roles/mount_device/README.md) +- [prometheus](roles/prometheus/README.md) - [ufw](roles/ufw/README.md) - [wg_hub](roles/wg_hub/README.md) \ No newline at end of file diff --git a/roles/prometheus/README.md b/roles/prometheus/README.md index 672f379..971b023 100644 --- a/roles/prometheus/README.md +++ b/roles/prometheus/README.md @@ -20,7 +20,7 @@ prometheus_group: prometheus # groups name prometheus_config_dir: "/etc/prometheus/conf" # where to place prometheus config prometheus_db_dir: "/var/lib/prometheus" # where to store prometheus db prometheus_config_source_dir: prometheus # path to config files on source -alertrules_source_dir: prometheus/rules # path to rule files on source +prometheus_alertrules_source_dir: prometheus/rules # path to rule files on source ``` Dependencies @@ -35,7 +35,7 @@ Example Playbook roles: - role: genlab.prometheus prometheus_config_source_dir: mydir/configs - alertrules_source_dir: mydir/configs/rules + prometheus_alertrules_source_dir: mydir/configs/rules prometheus_version: 3.2.1 ``` From 0561661811ecc77a2afaecbd6770996cdb7f2797 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 12:10:01 +0300 Subject: [PATCH 28/96] update galaxy version --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 6f50d1d..adcc287 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.5.0 +version: 0.6.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From db55cb16eb86004c170e2c4fa784ad4410ac2b22 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 14:17:20 +0300 Subject: [PATCH 29/96] fix linter errors --- roles/wg_spoke/.ansible-lint | 16 +++++++ roles/wg_spoke/.gitignore | 2 + roles/wg_spoke/.yamllint | 8 ++++ roles/wg_spoke/README.md | 46 ++++++++++++++++++++ roles/wg_spoke/conda.dev.yml | 11 +++++ roles/wg_spoke/conda.prod.yml | 9 ++++ roles/wg_spoke/defaults/main.yml | 3 ++ roles/wg_spoke/handlers/main.yml | 7 +++ roles/wg_spoke/meta/main.yml | 17 ++++++++ roles/wg_spoke/molecule/default/converge.yml | 20 +++++++++ roles/wg_spoke/molecule/default/molecule.yml | 27 ++++++++++++ roles/wg_spoke/molecule/default/verify.yml | 13 ++++++ roles/wg_spoke/renovate.json | 6 +++ roles/wg_spoke/requirements.ci.txt | 6 +++ roles/wg_spoke/requirements.txt | 1 + roles/wg_spoke/requirements.yml | 3 ++ roles/wg_spoke/tasks/deploy.yml | 41 +++++++++++++++++ roles/wg_spoke/tasks/logging.yml | 11 +++++ roles/wg_spoke/tasks/main.yml | 9 ++++ roles/wg_spoke/vars/main.yml | 1 + 20 files changed, 257 insertions(+) create mode 100644 roles/wg_spoke/.ansible-lint create mode 100644 roles/wg_spoke/.gitignore create mode 100644 roles/wg_spoke/.yamllint create mode 100644 roles/wg_spoke/README.md create mode 100644 roles/wg_spoke/conda.dev.yml create mode 100644 roles/wg_spoke/conda.prod.yml create mode 100644 roles/wg_spoke/defaults/main.yml create mode 100644 roles/wg_spoke/handlers/main.yml create mode 100644 roles/wg_spoke/meta/main.yml create mode 100644 roles/wg_spoke/molecule/default/converge.yml create mode 100644 roles/wg_spoke/molecule/default/molecule.yml create mode 100644 roles/wg_spoke/molecule/default/verify.yml create mode 100644 roles/wg_spoke/renovate.json create mode 100644 roles/wg_spoke/requirements.ci.txt create mode 100644 roles/wg_spoke/requirements.txt create mode 100644 roles/wg_spoke/requirements.yml create mode 100644 roles/wg_spoke/tasks/deploy.yml create mode 100644 roles/wg_spoke/tasks/logging.yml create mode 100644 roles/wg_spoke/tasks/main.yml create mode 100644 roles/wg_spoke/vars/main.yml diff --git a/roles/wg_spoke/.ansible-lint b/roles/wg_spoke/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/wg_spoke/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/wg_spoke/.gitignore b/roles/wg_spoke/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/wg_spoke/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/wg_spoke/.yamllint b/roles/wg_spoke/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/wg_spoke/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/wg_spoke/README.md b/roles/wg_spoke/README.md new file mode 100644 index 0000000..c0ba380 --- /dev/null +++ b/roles/wg_spoke/README.md @@ -0,0 +1,46 @@ +wg_spoke +========= + +Configure the host as a Spoke in Wireguard Spoke&Hub topology. + +Requirements +------------ + +Ubuntu 20.04, 22.04 or 24.04. + +Role Variables +-------------- + +```yaml +iface_name: name of the Wireguard interface (network), config file is named after it; WARNING, dashes are not supported +spoke_ipv4_vpn: IPv4 address that this Spoke should expect on the VPN +subnet: something like "10.0.0.0" +netmask: CIDR-style netmask without a slash, e.g. "24" +spoke_pkey: Spoke private key (Wireguard-generated, base64) +hub_pubkey: Hub public key (Wireguard-generated, base64) +hub_ipv4_wan: WAN (Internet) IPv4 address of the Hub +hub_ipv4_vpn: IPv4 address of the Hub on the VPN +hub_port: Hub should be listening on this port +wg_spoke_hide_secrets: whether or not prevent Ansible from logging tasks that handle sensitive info like keys (default is `true`) +wg_spoke_logging: whether or not to send Wireguard dyndbg logs to the kernel buffer accesible via journalctl -ek (default is `true`) +``` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See `molecule/default/converge.yml`. + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/wg_spoke/conda.dev.yml b/roles/wg_spoke/conda.dev.yml new file mode 100644 index 0000000..93ca437 --- /dev/null +++ b/roles/wg_spoke/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-wg-spoke +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/wg_spoke/conda.prod.yml b/roles/wg_spoke/conda.prod.yml new file mode 100644 index 0000000..239f515 --- /dev/null +++ b/roles/wg_spoke/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-wg-spoke +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/wg_spoke/defaults/main.yml b/roles/wg_spoke/defaults/main.yml new file mode 100644 index 0000000..3cb9c8b --- /dev/null +++ b/roles/wg_spoke/defaults/main.yml @@ -0,0 +1,3 @@ +--- +wg_spoke_hide_secrets: true +wg_spoke_logging: true diff --git a/roles/wg_spoke/handlers/main.yml b/roles/wg_spoke/handlers/main.yml new file mode 100644 index 0000000..7372f6c --- /dev/null +++ b/roles/wg_spoke/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "Run the Wireguard service" + ansible.builtin.systemd_service: + name: wg-quick@{{ iface_name }} + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/wg_spoke/meta/main.yml b/roles/wg_spoke/meta/main.yml new file mode 100644 index 0000000..396d62a --- /dev/null +++ b/roles/wg_spoke/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "wg_spoke" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy", "noble"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/wg_spoke/molecule/default/converge.yml b/roles/wg_spoke/molecule/default/converge.yml new file mode 100644 index 0000000..cc5f583 --- /dev/null +++ b/roles/wg_spoke/molecule/default/converge.yml @@ -0,0 +1,20 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.wg_spoke + iface_name: adm + subnet: "10.0.0.0" + netmask: "24" + spoke_ipv4_vpn: "10.0.0.1" + spoke_port: "51820" + spoke_pkey: eGqGx4A5ufFKatLflPFKNuzFgGuospIe08iPbTlEhm0= + hub_pubkey: EvcoZ21/p0AHz5y95jwjVIR9puljc2kXqh/f3U1A4nk= + # hub_psk: 5qmMuLuGQT7w8VnHPsZDO4vRF5wEKcpEtaCQW1BLmbg= + hub_ipv4_wan: "1.2.3.4" + hub_ipv4_vpn: "10.0.0.254" + hub_port: "51820" + dns_server: "10.0.0.254:5300" + domain: "adm.local" + wg_spoke_hide_secrets: false + wg_spoke_logging: false # doesn't work in Github Actions environment diff --git a/roles/wg_spoke/molecule/default/molecule.yml b/roles/wg_spoke/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/wg_spoke/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/wg_spoke/molecule/default/verify.yml b/roles/wg_spoke/molecule/default/verify.yml new file mode 100644 index 0000000..38f93ff --- /dev/null +++ b/roles/wg_spoke/molecule/default/verify.yml @@ -0,0 +1,13 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + iface_name: adm + + tasks: + - name: "Check that the Wireguard service is running" + ansible.builtin.service: + name: wg-quick@{{ iface_name }} + state: started diff --git a/roles/wg_spoke/renovate.json b/roles/wg_spoke/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/wg_spoke/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/wg_spoke/requirements.ci.txt b/roles/wg_spoke/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/wg_spoke/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/wg_spoke/requirements.txt b/roles/wg_spoke/requirements.txt new file mode 100644 index 0000000..cd252b9 --- /dev/null +++ b/roles/wg_spoke/requirements.txt @@ -0,0 +1 @@ +ansible~=11.1.0 diff --git a/roles/wg_spoke/requirements.yml b/roles/wg_spoke/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/wg_spoke/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/wg_spoke/tasks/deploy.yml b/roles/wg_spoke/tasks/deploy.yml new file mode 100644 index 0000000..faa60ad --- /dev/null +++ b/roles/wg_spoke/tasks/deploy.yml @@ -0,0 +1,41 @@ +--- +- name: "Install Wireguard system-wide" + ansible.builtin.apt: + name: wireguard + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Ensure no dashes in the interface name" + when: "'-' in iface_name" + ansible.builtin.fail: + msg: "The interface name must not contain dashes, got: '{{ iface_name }}'" + +- name: "Create a Spoke configuration file" + no_log: "{{ wg_spoke_hide_secrets }}" + notify: "Run the Wireguard service" + ansible.builtin.blockinfile: + path: "/etc/wireguard/{{ iface_name }}.conf" + create: true + owner: root + group: root + mode: "0600" + state: present + block: | + [Interface] # Local settings for this node + Address = {{ spoke_ipv4_vpn }}/32 + ListenPort = {{ spoke_port }} + PrivateKey = {{ spoke_pkey }} + + {% if dns_server is defined and domain is defined %} + PostUp = resolvectl dns %i {{ dns_server }}; resolvectl domain %i {{ domain }} + {% endif %} + + [Peer] # Settings for the Hub peer + PublicKey = {{ hub_pubkey }} + {% if spoke_psk is defined %} + PresharedKey = {{ spoke_psk }} + {% endif %} + AllowedIPs = {{ subnet }}/{{ netmask }} + Endpoint = {{ hub_ipv4_wan }}:{{ hub_port }} + PersistentKeepalive = 20 diff --git a/roles/wg_spoke/tasks/logging.yml b/roles/wg_spoke/tasks/logging.yml new file mode 100644 index 0000000..8989ae4 --- /dev/null +++ b/roles/wg_spoke/tasks/logging.yml @@ -0,0 +1,11 @@ +--- +# SRC https://www.procustodibus.com/blog/2021/03/wireguard-logs/ +- name: "Add wireguard module" + community.general.modprobe: + name: wireguard + state: present + +- name: "Turn on Wireguard dyndbg logging into kernel message buffer" + changed_when: false + ansible.builtin.shell: + cmd: echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control diff --git a/roles/wg_spoke/tasks/main.yml b/roles/wg_spoke/tasks/main.yml new file mode 100644 index 0000000..d25fec3 --- /dev/null +++ b/roles/wg_spoke/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: "Include Wireguard deployment tasks" + ansible.builtin.include_tasks: + file: "tasks/deploy.yml" + +- name: "Include Wireguard logging configuration tasks" + when: wg_spoke_logging is true + ansible.builtin.include_tasks: + file: "tasks/logging.yml" diff --git a/roles/wg_spoke/vars/main.yml b/roles/wg_spoke/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/wg_spoke/vars/main.yml @@ -0,0 +1 @@ +--- From 4503751e8718393fe21fbd0ee566de776a455f3a Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 14:18:07 +0300 Subject: [PATCH 30/96] remove garbage --- roles/wg_spoke/.ansible-lint | 16 ---------------- roles/wg_spoke/.gitignore | 2 -- roles/wg_spoke/.yamllint | 8 -------- roles/wg_spoke/conda.dev.yml | 11 ----------- roles/wg_spoke/conda.prod.yml | 9 --------- roles/wg_spoke/renovate.json | 6 ------ roles/wg_spoke/requirements.ci.txt | 6 ------ roles/wg_spoke/requirements.txt | 1 - roles/wg_spoke/requirements.yml | 3 --- 9 files changed, 62 deletions(-) delete mode 100644 roles/wg_spoke/.ansible-lint delete mode 100644 roles/wg_spoke/.gitignore delete mode 100644 roles/wg_spoke/.yamllint delete mode 100644 roles/wg_spoke/conda.dev.yml delete mode 100644 roles/wg_spoke/conda.prod.yml delete mode 100644 roles/wg_spoke/renovate.json delete mode 100644 roles/wg_spoke/requirements.ci.txt delete mode 100644 roles/wg_spoke/requirements.txt delete mode 100644 roles/wg_spoke/requirements.yml diff --git a/roles/wg_spoke/.ansible-lint b/roles/wg_spoke/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/wg_spoke/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/wg_spoke/.gitignore b/roles/wg_spoke/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/wg_spoke/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/wg_spoke/.yamllint b/roles/wg_spoke/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/wg_spoke/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/wg_spoke/conda.dev.yml b/roles/wg_spoke/conda.dev.yml deleted file mode 100644 index 93ca437..0000000 --- a/roles/wg_spoke/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-wg-spoke -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/wg_spoke/conda.prod.yml b/roles/wg_spoke/conda.prod.yml deleted file mode 100644 index 239f515..0000000 --- a/roles/wg_spoke/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-wg-spoke -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/wg_spoke/renovate.json b/roles/wg_spoke/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/wg_spoke/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/wg_spoke/requirements.ci.txt b/roles/wg_spoke/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/wg_spoke/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/wg_spoke/requirements.txt b/roles/wg_spoke/requirements.txt deleted file mode 100644 index cd252b9..0000000 --- a/roles/wg_spoke/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.1.0 diff --git a/roles/wg_spoke/requirements.yml b/roles/wg_spoke/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/wg_spoke/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From 46a8f6bee2e7d6bdb366e5a7d92088155937ac56 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 14:26:32 +0300 Subject: [PATCH 31/96] add wg_spoke to readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e571129..cfc2e2d 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,5 @@ - [mount_device](roles/mount_device/README.md) - [prometheus](roles/prometheus/README.md) - [ufw](roles/ufw/README.md) -- [wg_hub](roles/wg_hub/README.md) \ No newline at end of file +- [wg_hub](roles/wg_hub/README.md) +- [wg_spoke](roles/wg_spoke/README.md) \ No newline at end of file From 1a94a50f8986b60bccd9cc51703bbdfca30047b9 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 14:26:47 +0300 Subject: [PATCH 32/96] update galaxy version --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index adcc287..02c97f7 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.6.0 +version: 0.7.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From b1bfcfb5982b85253c4fc827104435ee7715bbba Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 14:32:28 +0300 Subject: [PATCH 33/96] add grafana role --- roles/grafana/.ansible-lint | 19 + roles/grafana/.yamllint | 8 + roles/grafana/README.md | 107 + roles/grafana/ansible-navigator.log | 0 roles/grafana/conda.dev.yml | 11 + roles/grafana/conda.prod.yml | 9 + roles/grafana/defaults/main.yml | 10 + roles/grafana/handlers/main.yml | 7 + roles/grafana/meta/main.yml | 17 + roles/grafana/molecule/default/converge.yml | 44 + .../default/dashboards/test_dashboard.json | 700 ++++++ .../default/dashboards/test_dashboard2.json | 2081 +++++++++++++++++ .../datasources/test_datasource_loki.yaml | 16 + .../test_datasource_prometheus.yaml | 50 + roles/grafana/molecule/default/molecule.yml | 29 + .../molecule/default/secrets/admin_cred.yml | 3 + roles/grafana/molecule/default/verify.yml | 41 + roles/grafana/renovate.json | 6 + roles/grafana/requirements.ci.txt | 6 + roles/grafana/requirements.txt | 1 + roles/grafana/requirements.yml | 4 + roles/grafana/tasks/dashboards.yml | 40 + roles/grafana/tasks/datasources.yml | 28 + roles/grafana/tasks/import_pub_dashboard.yml | 11 + roles/grafana/tasks/install.yml | 78 + roles/grafana/tasks/main.yml | 39 + roles/grafana/tasks/plugins.yml | 16 + roles/grafana/tasks/user.yml | 19 + roles/grafana/templates/grafana-server.env.j2 | 24 + roles/grafana/vars/main.yml | 1 + 30 files changed, 3425 insertions(+) create mode 100644 roles/grafana/.ansible-lint create mode 100644 roles/grafana/.yamllint create mode 100644 roles/grafana/README.md create mode 100644 roles/grafana/ansible-navigator.log create mode 100644 roles/grafana/conda.dev.yml create mode 100644 roles/grafana/conda.prod.yml create mode 100644 roles/grafana/defaults/main.yml create mode 100644 roles/grafana/handlers/main.yml create mode 100644 roles/grafana/meta/main.yml create mode 100644 roles/grafana/molecule/default/converge.yml create mode 100644 roles/grafana/molecule/default/dashboards/test_dashboard.json create mode 100644 roles/grafana/molecule/default/dashboards/test_dashboard2.json create mode 100644 roles/grafana/molecule/default/datasources/test_datasource_loki.yaml create mode 100644 roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml create mode 100644 roles/grafana/molecule/default/molecule.yml create mode 100644 roles/grafana/molecule/default/secrets/admin_cred.yml create mode 100644 roles/grafana/molecule/default/verify.yml create mode 100644 roles/grafana/renovate.json create mode 100644 roles/grafana/requirements.ci.txt create mode 100644 roles/grafana/requirements.txt create mode 100644 roles/grafana/requirements.yml create mode 100644 roles/grafana/tasks/dashboards.yml create mode 100644 roles/grafana/tasks/datasources.yml create mode 100644 roles/grafana/tasks/import_pub_dashboard.yml create mode 100644 roles/grafana/tasks/install.yml create mode 100644 roles/grafana/tasks/main.yml create mode 100644 roles/grafana/tasks/plugins.yml create mode 100644 roles/grafana/tasks/user.yml create mode 100644 roles/grafana/templates/grafana-server.env.j2 create mode 100644 roles/grafana/vars/main.yml diff --git a/roles/grafana/.ansible-lint b/roles/grafana/.ansible-lint new file mode 100644 index 0000000..23e8eb6 --- /dev/null +++ b/roles/grafana/.ansible-lint @@ -0,0 +1,19 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ + - molecule/default/secrets/ + - molecule/default/datasources/ + - molecule/default/dashboards/ diff --git a/roles/grafana/.yamllint b/roles/grafana/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/grafana/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/grafana/README.md b/roles/grafana/README.md new file mode 100644 index 0000000..5181eb8 --- /dev/null +++ b/roles/grafana/README.md @@ -0,0 +1,107 @@ +genlab.grafana +========= + +This ansible role installs [Grafana](https://github.com/grafana/grafana) - the open-source platform for monitoring and observability. It can produce charts, graphs, and alerts for the web when connected to supported data sources. + +This role installs and configures Grafana from a binary distribution. It also: + +- Uploads custom dashboards + +- Installs plugins + +- Imports public dashboards and data sources + +- Allows admin password changes + +Supports user creation + Grafana service requires an environment file at startup, where you can set custom paths for logs (`log_dir`), data (`data_dir`), and plugins (`plugins_dir`). This allows flexible control over Grafana's data storage. + + Requirements +------------ + +You need `community.grafana` module. + +Role Variables +-------------- + +``` +grafana_user: "grafana" # user name +grafana_group: "grafana" # group name +grafana_version: 11.5.0 # version +grafana_port: 3000 # port + +# Directory paths +dashboard_dir: "/tmp/grafana/dashboards" # where to copy dashboards from source +plugins_dir: "/var/lib/grafana/plugins" # where to store plugins on target +datasource_dir: "/etc/grafana/provisioning/datasources" # where to store data sources on target +log_dir: "/var/log/grafana" # where to write logs +data_dir: "/var/lib/grafana" # where to store Grafana DB + +# Optional configurations +grafana_users: [] # array of user names, passwords, and statuses +grafana_plugins: [] # array of plugins to install +grafana_public_dashboards: [] # array of public dashboards to import + +# Admin credentials +admin_api_username: "secret" # Grafana admin username +admin_api_password: "secret" # Grafana admin password + +# Source paths +dashboard_source_path: "mydir/dashboards" # path to dashboards on source server +datasource_source_path: "mydir/datasources" # path to data sources on source server +``` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +--- +- name: Converge + hosts: all + vars: + grafana_users: + - name: "test" + user_login: "test" + user_password: "test" + user_email: "test@mail.ru" + is_admin: false + grafana_plugins: + version: 2.1.8 + - name: aceiot-svg-panel + version: 0.1.5 + grafana_public_dashboards: + - name: Node Full Exporter + id: 1860 + revision: 36 + vars_files: + - secrets/admin_cred.yml + + roles: + - role: genlab.grafana + grafana_version: 11.5.0 + admin_api_username: "{{ grafana.admin_api_username }}" + admin_api_password: "{{ grafana.admin_api_password }}" + users: "{{ grafana_users }}" + plugins: "{{ grafana_plugins }}" + public_dashboards: "{{ grafana_public_dashboards }}" + dashboard_source_path: "molecule/default/dashboards" + datasource_source_path: "molecule/default/datasources" + log_dir: "/opt/grafana/data" + data_dir: "/opt/grafana/lib" + +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/grafana/ansible-navigator.log b/roles/grafana/ansible-navigator.log new file mode 100644 index 0000000..e69de29 diff --git a/roles/grafana/conda.dev.yml b/roles/grafana/conda.dev.yml new file mode 100644 index 0000000..fb974b7 --- /dev/null +++ b/roles/grafana/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/grafana/conda.prod.yml b/roles/grafana/conda.prod.yml new file mode 100644 index 0000000..d6d12d6 --- /dev/null +++ b/roles/grafana/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/grafana/defaults/main.yml b/roles/grafana/defaults/main.yml new file mode 100644 index 0000000..aa32a25 --- /dev/null +++ b/roles/grafana/defaults/main.yml @@ -0,0 +1,10 @@ +--- +grafana_user: "grafana" +grafana_group: "grafana" +grafana_version: 11.5.0 +grafana_port: 3000 +dashboard_dir: "/tmp/grafana/dashboards" +plugins_dir: "/var/lib/grafana/plugins" +datasource_dir: "/etc/grafana/provisioning/datasources" +log_dir: "/var/log/grafana" +data_dir: "/var/lib/grafana" diff --git a/roles/grafana/handlers/main.yml b/roles/grafana/handlers/main.yml new file mode 100644 index 0000000..0b67d92 --- /dev/null +++ b/roles/grafana/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "(Re)start and enable Grafana" + ansible.builtin.systemd_service: + name: grafana-server + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/grafana/meta/main.yml b/roles/grafana/meta/main.yml new file mode 100644 index 0000000..7d3782f --- /dev/null +++ b/roles/grafana/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "grafana" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/grafana/molecule/default/converge.yml b/roles/grafana/molecule/default/converge.yml new file mode 100644 index 0000000..136f248 --- /dev/null +++ b/roles/grafana/molecule/default/converge.yml @@ -0,0 +1,44 @@ +--- +- name: Converge + hosts: all + vars: + grafana_users: + - name: "test" + user_login: "test" + # kics-scan ignore-line + user_password: "test" + user_email: "test@mail.ru" + is_admin: false + - name: "test2" + user_login: "test2" + # kics-scan ignore-line + user_password: "test2" + user_email: "test2@mail.ru" + is_admin: true + grafana_plugins: + - name: grafana-metricsdrilldown-app + version: 1.0.0 + - name: grafana-clock-panel + version: 2.1.8 + - name: aceiot-svg-panel + version: 0.1.5 + grafana_public_dashboards: + - name: Node Full Exporter + id: 1860 + revision: 36 + vars_files: + - secrets/admin_cred.yml + + roles: + # kics-scan ignore-block + - role: genlab.grafana + grafana_version: 11.5.0 + admin_api_username: "{{ grafana.admin_api_username }}" + admin_api_password: "{{ grafana.admin_api_password }}" + users: "{{ grafana_users }}" + plugins: "{{ grafana_plugins }}" + public_dashboards: "{{ grafana_public_dashboards }}" + dashboard_source_path: "molecule/default/dashboards" + datasource_source_path: "molecule/default/datasources" + log_dir: "/opt/grafana/data" + data_dir: "/opt/grafana/lib" diff --git a/roles/grafana/molecule/default/dashboards/test_dashboard.json b/roles/grafana/molecule/default/dashboards/test_dashboard.json new file mode 100644 index 0000000..595932e --- /dev/null +++ b/roles/grafana/molecule/default/dashboards/test_dashboard.json @@ -0,0 +1,700 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.1.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 29, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "decimals": 0, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 15, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.1.5", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", namespace=~\"$namespace\", status=~\"fail|error\" } > 0) by (namespace)", + "instant": true, + "interval": "", + "legendFormat": "{{namespace}}", + "refId": "A" + } + ], + "title": "Failing Policies by Namespace", + "type": "bargauge" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "decimals": 0, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 3 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 15, + "y": 0 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "vertical", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.1.5", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(cluster_policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", status=~\"fail|error\" } > 0) by (status)", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{ status }}", + "refId": "A" + } + ], + "title": "Failing ClusterPolicies", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": true, + "hideZero": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "10.1.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(cluster_policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", status=~\"fail|error\" } > 0) by (policy)", + "interval": "", + "legendFormat": "{{ policy }}", + "refId": "A" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", namespace=~\"$namespace\", status=~\"fail|error\" } > 0) by (policy)", + "interval": "", + "legendFormat": "{{ policy }}", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Failing Policies Graph", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 7, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.1.5", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", namespace=~\"$namespace\", status=~\"fail|error\" }) by (namespace,policy,rule,kind,name,status,category,severity,source)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{namespace}}: {{ policy }}", + "refId": "A" + } + ], + "title": "Failing PolicyRules", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true + }, + "indexByName": { + "category": 1, + "kind": 4, + "name": 5, + "namespace": 3, + "policy": 6, + "rule": 7, + "severity": 2, + "source": 0, + "status": 8 + }, + "renameByName": { + "namespace": "namespace" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "custom": { + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 9, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.1.5", + "targets": [ + { + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(cluster_policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", status=~\"fail|error\" }) by (policy,rule,kind,name,status,category,severity,source)", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{ kind }}: {{ name }} - {{ policy }}", + "refId": "A" + } + ], + "title": "Failing ClusterPolicyRules", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true, + "__name__": true, + "container": true, + "endpoint": true, + "instance": true, + "job": true, + "namespace": true, + "pod": true, + "report": true, + "service": true + }, + "indexByName": { + "category": 1, + "kind": 3, + "name": 4, + "policy": 5, + "rule": 6, + "severity": 2, + "source": 0, + "status": 7 + }, + "renameByName": {} + } + } + ], + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [ + "Policy Reporter" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "prometheus" + }, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, policy)", + "hide": 0, + "includeAll": true, + "label": "Policy", + "multi": true, + "name": "policy", + "options": [], + "query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, policy)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, category)", + "hide": 0, + "includeAll": true, + "label": "Category", + "multi": true, + "name": "category", + "options": [], + "query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, category)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, severity)", + "hide": 0, + "includeAll": true, + "label": "Severity", + "multi": true, + "name": "severity", + "options": [], + "query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, severity)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({__name__= \"policy_report_result\", status=~\"fail|error\"}, namespace)", + "hide": 0, + "includeAll": true, + "label": "Namespace", + "multi": true, + "name": "namespace", + "options": [], + "query": "label_values({__name__= \"policy_report_result\", status=~\"fail|error\"}, namespace)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, kind)", + "hide": 0, + "includeAll": true, + "label": "Kind", + "multi": true, + "name": "kind", + "options": [], + "query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, kind)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, source)", + "hide": 0, + "includeAll": true, + "label": "Source", + "multi": true, + "name": "source", + "options": [], + "query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, source)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "PolicyReports", + "uid": "ZkwXrUMnk", + "version": 1, + "gnetId": 13968 +} \ No newline at end of file diff --git a/roles/grafana/molecule/default/dashboards/test_dashboard2.json b/roles/grafana/molecule/default/dashboards/test_dashboard2.json new file mode 100644 index 0000000..a100af0 --- /dev/null +++ b/roles/grafana/molecule/default/dashboards/test_dashboard2.json @@ -0,0 +1,2081 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.6" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart v2", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "A dashboard for the CoreDNS DNS server with updated metrics for version 1.7.0+. Based on the CoreDNS 1.7.0+ dashboard by ejkinger", + "editable": true, + "gnetId": 14981, + "graphTooltip": 0, + "id": null, + "iteration": 1632672605392, + "links": [ + { + "$$hashKey": "object:94", + "icon": "external link", + "tags": [], + "targetBlank": true, + "title": "CoreDNS.io", + "type": "link", + "url": "https://coredns.io" + } + ], + "panels": [ + { + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 44, + "title": "Global stats", + "type": "row" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 39, + "links": [], + "options": { + "displayLabels": [ + "percent" + ], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_dns_requests_total[5m])) by (instance)", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{instance}}", + "refId": "A", + "step": 60 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (by instance)", + "type": "piechart" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 41, + "panels": [], + "repeat": "instance", + "title": "Health: $instance", + "type": "row" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "super-light-blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 10 + }, + "id": 42, + "links": [], + "maxPerRow": 2, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "7.5.6", + "repeat": null, + "repeatDirection": "v", + "targets": [ + { + "exemplar": true, + "expr": "coredns_build_info{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{version}}", + "refId": "A", + "step": 60 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Version", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 6, + "y": 10 + }, + "id": 35, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_forward_healthcheck_broken_total{instance=~\"$instance\"}[5m]))", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Upstream Health Check Fails", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 9, + "y": 10 + }, + "id": 36, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_forward_max_concurrent_rejects_total{instance=~\"$instance\"}[5m]))", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "title": "Upstream Rejected Queries", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 12, + "y": 10 + }, + "id": 81, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_panics_total{instance=~\"$instance\"}[5m]))", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panics", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 15, + "y": 10 + }, + "id": 92, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_reload_failed_total{instance=~\"$instance\"}[5m]))", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Failed Reloads", + "type": "stat" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 0.03, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 60 + }, + { + "color": "red", + "value": 85 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 10 + }, + "id": 119, + "options": { + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(process_cpu_seconds_total{instance=~\"$instance\"}[5m]))", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU Time", + "type": "gauge" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "super-light-blue", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 21, + "y": 10 + }, + "id": 134, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "go_memstats_alloc_bytes{instance=~\"$instance\"}", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 26, + "panels": [], + "repeat": null, + "title": "Local", + "type": "row" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 2, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_dns_requests_total{instance=~\"$instance\"}[5m])) by (server)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{server}}", + "refId": "A", + "step": 60 + }, + { + "exemplar": true, + "expr": "sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "cache", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (total)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 6, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_dns_requests_total{instance=~\"$instance\"}[5m])) by (zone)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{zone}}", + "refId": "A", + "step": 60 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (by zone)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 38 + }, + "id": 32, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(coredns_dns_request_duration_seconds_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.90, sum(rate(coredns_dns_request_duration_seconds_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "90%", + "refId": "B", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.50, sum(rate(coredns_dns_request_duration_seconds_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "50%", + "refId": "C", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Responses (latency, internet zone)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 38 + }, + "id": 4, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_dns_requests_total{instance=~\"$instance\"}[5m])) by (type)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 60 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (by type)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 45 + }, + "id": 24, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_cache_hits_total{instance=~\"$instance\", type=\"success\"}[5m])) / sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "hits: success", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "sum(rate(coredns_cache_hits_total{instance=~\"$instance\", type=\"denial\"}[5m])) / sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "hits: denial", + "refId": "B", + "step": 40 + }, + { + "exemplar": true, + "expr": "(sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m])) - sum(rate(coredns_cache_hits_total{instance=~\"$instance\", type=\"success\"}[5m]))) / sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "misses", + "refId": "C" + }, + { + "exemplar": true, + "expr": "sum(rate(coredns_dnssec_cache_hits_total{instance=~\"$instance\"}[5m])) / sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "hits: DNSSEC", + "refId": "D" + }, + { + "exemplar": true, + "expr": "(sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m])) - sum(rate(coredns_dnssec_cache_hits_total{instance=~\"$instance\"}[5m]))) / sum(rate(coredns_cache_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "legendFormat": "misses: DNSSEC", + "refId": "E" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Cache (hitrate)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 45 + }, + "id": 8, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_dns_do_requests_total{instance=~\"$instance\"}[5m])) by (zone)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{zone}}", + "refId": "A", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (DNSSEC by zone)", + "type": "timeseries" + }, + { + "cacheTimeout": null, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 52 + }, + "id": 14, + "interval": null, + "links": [], + "options": { + "displayLabels": [], + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "values": [ + "value", + "percent" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_dns_responses_total{instance=~\"$instance\"}[5m])) by (rcode)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{rcode}}", + "refId": "A", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Responses (by code)", + "type": "piechart" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 52 + }, + "id": 18, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(coredns_dns_request_size_bytes_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "interval": "", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.90, sum(rate(coredns_dns_request_size_bytes_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "90%", + "refId": "B", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.50, sum(rate(coredns_dns_request_size_bytes_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "50%", + "metric": "", + "refId": "C", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (size, internet zone)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 52 + }, + "id": 33, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(coredns_dns_response_size_bytes_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "interval": "", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.90, sum(rate(coredns_dns_response_size_bytes_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "90%", + "refId": "B", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.50, sum(rate(coredns_dns_response_size_bytes_bucket{instance=~\"$instance\", zone=\".\"}[5m])) by (le))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "50%", + "metric": "", + "refId": "C", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Responses (size, internet zone)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 59 + }, + "id": 22, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(coredns_cache_entries{instance=~\"$instance\"}) by (type)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{type}}", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "sum(coredns_dnssec_cache_entries{instance=~\"$instance\"})", + "hide": false, + "interval": "", + "legendFormat": "DNSSEC", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Cache (size)", + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 66 + }, + "id": 63, + "panels": [], + "title": "Upstream", + "type": "row" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 67 + }, + "id": 72, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_forward_requests_total{instance=~\"$instance\"}[5m]))", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "upstream", + "refId": "A", + "step": 60 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (total)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 67 + }, + "id": 38, + "links": [], + "maxPerRow": 6, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "repeat": null, + "repeatDirection": "h", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_forward_conn_cache_hits_total{instance=~\"$instance\"}[5m])) / sum(rate(coredns_forward_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "hits", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "(sum(rate(coredns_forward_requests_total{instance=~\"$instance\"}[5m])) - sum(rate(coredns_forward_conn_cache_hits_total{instance=~\"$instance\"}[5m]))) / sum(rate(coredns_forward_requests_total{instance=~\"$instance\"}[5m]))", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "misses", + "refId": "B", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Cache (hitrate)", + "type": "timeseries" + }, + { + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 74 + }, + "id": 37, + "links": [], + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "multi" + } + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(coredns_forward_request_duration_seconds_bucket{instance=~\"$instance\"}[5m])) by (le))", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "99%", + "refId": "A", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.90, sum(rate(coredns_forward_request_duration_seconds_bucket{instance=~\"$instance\"}[5m])) by (le))", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "90%", + "refId": "B", + "step": 40 + }, + { + "exemplar": true, + "expr": "histogram_quantile(0.50, sum(rate(coredns_forward_request_duration_seconds_bucket{instance=~\"$instance\"}[5m])) by (le))", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "50%", + "refId": "C", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Responses (latency)", + "type": "timeseries" + }, + { + "cacheTimeout": null, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 74 + }, + "id": 105, + "interval": null, + "links": [], + "options": { + "displayLabels": [ + "percent" + ], + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_forward_requests_total{instance=~\"$instance\"}[5m])) by (to)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{to}}", + "refId": "A", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests (by upstream)", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "1.0.0.1:853", + "1.1.1.1:853", + "8.8.4.4:853", + "8.8.8.8:853" + ] + } + } + } + ], + "type": "piechart" + }, + { + "cacheTimeout": null, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 74 + }, + "id": 53, + "interval": null, + "links": [], + "options": { + "displayLabels": [], + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "values": [ + "value", + "percent" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.6", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(coredns_forward_responses_total{instance=~\"$instance\"}[5m])) by (rcode)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{rcode}}", + "refId": "A", + "step": 40 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Responses (by code)", + "type": "piechart" + } + ], + "refresh": "5s", + "schemaVersion": 27, + "style": "dark", + "tags": [ + "dns", + "coredns" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(up{job=\"coredns\"}, instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Instance", + "multi": true, + "name": "instance", + "options": [], + "query": { + "query": "label_values(up{job=\"coredns\"}, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 3, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "CoreDNS", + "uid": "wY4blRMGz", + "version": 134 +} \ No newline at end of file diff --git a/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml b/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml new file mode 100644 index 0000000..335bacc --- /dev/null +++ b/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml @@ -0,0 +1,16 @@ +apiVersion: 1 + +datasources: + - name: Loki + type: loki + access: proxy + basicAuth: false + basicAuthPassword: false + url: http://localhost:3100 + jsonData: + maxLines: 1000 + httpHeaderName1: "Authorization" + secureJsonData: + httpHeaderValue1: "Bearer your_token_here" + version: 1 + editable: false \ No newline at end of file diff --git a/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml b/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml new file mode 100644 index 0000000..5e334f1 --- /dev/null +++ b/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml @@ -0,0 +1,50 @@ +# config file version +apiVersion: 1 + +# list of datasources that should be deleted from the database +# deleteDatasources: +# - name: Prometheus +# orgId: 1 + +# list of datasources to insert/update depending +# whats available in the database +datasources: + # name of the datasource. Required +- name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://localhost:9090 + # database password, if used + password: + # database user, if used + user: + # database name, if used + database: + # enable/disable basic auth + basicAuth: false + # basic auth username, if used + basicAuthUser: + # basic auth password, if used + basicAuthPassword: + # enable/disable with credentials headers + withCredentials: + # mark as default datasource. Max one per org + isDefault: true + # fields that will be converted to json and stored in json_data + jsonData: + graphiteVersion: "1.1" + tlsAuth: false + tlsAuthWithCACert: false + # json object of data that will be encrypted. + secureJsonData: + tlsCACert: "..." + tlsClientCert: "..." + tlsClientKey: "..." + version: 1 + # allow users to edit datasources from the UI. + editable: true \ No newline at end of file diff --git a/roles/grafana/molecule/default/molecule.yml b/roles/grafana/molecule/default/molecule.yml new file mode 100644 index 0000000..1e73d79 --- /dev/null +++ b/roles/grafana/molecule/default/molecule.yml @@ -0,0 +1,29 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + published_ports: + - 127.0.0.1:3001:3000 + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/grafana/molecule/default/secrets/admin_cred.yml b/roles/grafana/molecule/default/secrets/admin_cred.yml new file mode 100644 index 0000000..7d48371 --- /dev/null +++ b/roles/grafana/molecule/default/secrets/admin_cred.yml @@ -0,0 +1,3 @@ +grafana: + admin_api_username: "admin" + admin_api_password: "changme23" diff --git a/roles/grafana/molecule/default/verify.yml b/roles/grafana/molecule/default/verify.yml new file mode 100644 index 0000000..41c2100 --- /dev/null +++ b/roles/grafana/molecule/default/verify.yml @@ -0,0 +1,41 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars_files: + - admin_cred.yml + + tasks: + + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Check if Grafana is installed" + changed_when: false + ansible.builtin.command: "grafana-server -v" + register: grafana_installed_version + + - name: "Check Grafana version" + ansible.builtin.assert: + that: "grafana_installed_version.stdout is regex('{{ grafana_version }}')" + success_msg: "grafana version {{ grafana_version }} is installed and working" + fail_msg: "grafana version {{ grafana_version }} is not installed or not working correctly" + + # kics-scan ignore-block + - name: "Check if Grafana login page is reachable" + ansible.builtin.uri: + url: "http://localhost:{{ grafana_port }}/api/health" + return_content: true + status_code: 200 + method: GET + body_format: json + register: grafana_health + + - name: "Debug Grafana health status" + ansible.builtin.assert: + that: grafana_health.json.database == "ok" + success_msg: "Grafana is healthy" + fail_msg: "Grafana is not healthy" diff --git a/roles/grafana/renovate.json b/roles/grafana/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/grafana/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/grafana/requirements.ci.txt b/roles/grafana/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/grafana/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/grafana/requirements.txt b/roles/grafana/requirements.txt new file mode 100644 index 0000000..f3b3882 --- /dev/null +++ b/roles/grafana/requirements.txt @@ -0,0 +1 @@ +ansible~=11.2.0 diff --git a/roles/grafana/requirements.yml b/roles/grafana/requirements.yml new file mode 100644 index 0000000..2e509b8 --- /dev/null +++ b/roles/grafana/requirements.yml @@ -0,0 +1,4 @@ +# requirements file +--- +collections: + - name: community.grafana diff --git a/roles/grafana/tasks/dashboards.yml b/roles/grafana/tasks/dashboards.yml new file mode 100644 index 0000000..2c1a7ff --- /dev/null +++ b/roles/grafana/tasks/dashboards.yml @@ -0,0 +1,40 @@ +--- +- name: "Create dashboard directory" + ansible.builtin.file: + path: "{{ dashboard_dir }}" + state: directory + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0755" + +- name: "Copy dashboard files from source to target" + ansible.builtin.copy: + src: "{{ item }}" + dest: "{{ dashboard_dir }}/{{ item | basename }}" + mode: "0644" + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + with_fileglob: + - "{{ dashboard_source_path }}/*.json" + +- name: "Import Grafana dashboards to Grafana" + community.grafana.grafana_dashboard: + grafana_url: "http://127.0.0.1:{{ grafana_port }}" + url_username: "{{ admin_api_username }}" + url_password: "{{ admin_api_password }}" + state: present + commit_message: Updated by ansible + overwrite: false + path: "{{ dashboard_dir }}/{{ item | basename }}" + with_fileglob: + - "{{ dashboard_source_path }}/*.json" + +- name: "Run | reload Grafana provisioned dashboard configurations" + ansible.builtin.uri: + # kics-scan ignore-line + url: "http://127.0.0.1:{{ grafana_port }}/api/admin/provisioning/dashboards/reload" + method: POST + force_basic_auth: true + user: "{{ admin_api_username }}" + password: "{{ admin_api_password }}" + status_code: 200 diff --git a/roles/grafana/tasks/datasources.yml b/roles/grafana/tasks/datasources.yml new file mode 100644 index 0000000..c5a2efe --- /dev/null +++ b/roles/grafana/tasks/datasources.yml @@ -0,0 +1,28 @@ +--- +- name: "Create datasource directory" + ansible.builtin.file: + path: "{{ datasource_dir }}" + state: directory + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0755" + +- name: "Configure | provision datasources for Grafana" + ansible.builtin.copy: + src: "{{ datasource_source_path }}/{{ item | basename }}" + dest: "{{ datasource_dir }}/{{ item | basename }}" + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0660" + with_fileglob: + "{{ datasource_source_path }}/*.y*ml" + +- name: "Run | reload Grafana datasource provisioned configurations" + ansible.builtin.uri: + # kics-scan ignore-line + url: "http://127.0.0.1:{{ grafana_port }}/api/admin/provisioning/datasources/reload" + method: POST + force_basic_auth: true + user: "{{ admin_api_username }}" + password: "{{ admin_api_password }}" + status_code: 200 diff --git a/roles/grafana/tasks/import_pub_dashboard.yml b/roles/grafana/tasks/import_pub_dashboard.yml new file mode 100644 index 0000000..13aead0 --- /dev/null +++ b/roles/grafana/tasks/import_pub_dashboard.yml @@ -0,0 +1,11 @@ +--- +- name: "Import public dashboard - '{{ public_dashboard.name }}''" + community.grafana.grafana_dashboard: + grafana_url: "http://127.0.0.1:{{ grafana_port }}" + state: "{{ public_dashboard.state | default('present') }}" + overwrite: false + dashboard_id: "{{ public_dashboard.id }}" + dashboard_revision: "{{ public_dashboard.revision }}" + commit_message: "Add public dashboard '{{ public_dashboard.name }}''" + url_username: "{{ admin_api_username }}" + url_password: "{{ admin_api_password }}" diff --git a/roles/grafana/tasks/install.yml b/roles/grafana/tasks/install.yml new file mode 100644 index 0000000..e79e378 --- /dev/null +++ b/roles/grafana/tasks/install.yml @@ -0,0 +1,78 @@ +--- +- name: "Create Grafana system group" + ansible.builtin.group: + name: grafana + system: true + state: present + +- name: "Create Grafana system user" + ansible.builtin.user: + name: grafana + group: grafana + system: true + shell: "/sbin/nologin" + create_home: false + state: present + +- name: "Install Grafana deb package" + block: + - name: "Check Grafana version" + changed_when: false + ansible.builtin.command: + cmd: "grafana-server --version" + register: grafana_ver + + - name: "Assert version correctness" + ansible.builtin.assert: + that: "grafana_ver.stdout is regex('{{ grafana_version }}')" + success_msg: "grafana version {{ grafana_version }} is installed and working" + fail_msg: "grafana version {{ grafana_version }} is not installed or not working correctly" + + rescue: + - name: "Ensure that directories exist" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: '0775' + with_items: + - "{{ log_dir }}" + - "{{ data_dir }}" + - "{{ plugins_dir }}" + + - name: "Download Grafana DEB package" + ansible.builtin.get_url: + url: "https://dl.grafana.com/oss/release/grafana_{{ grafana_version }}_amd64.deb" + dest: "/tmp/grafana-{{ grafana_version }}_amd64.deb" + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0644" + register: package_tmp + + - name: "Install DEB package" + notify: "(Re)start and enable Grafana" + ansible.builtin.apt: + deb: "{{ package_tmp.dest }}" + state: present + update_cache: true + + - name: "Cleanup downloaded file" + ansible.builtin.file: + path: "/tmp/grafana-{{ grafana_version }}_amd64.deb" + state: absent + + - name: "Create env file for systemd service unit" + notify: "(Re)start and enable Grafana" + ansible.builtin.template: + src: grafana-server.env.j2 + dest: "{{ item }}" + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0660" + with_items: + - "/etc/default/grafana-server" + - "/etc/default/grafana" + +- name: "Flush handlers" + ansible.builtin.meta: "flush_handlers" diff --git a/roles/grafana/tasks/main.yml b/roles/grafana/tasks/main.yml new file mode 100644 index 0000000..75cba65 --- /dev/null +++ b/roles/grafana/tasks/main.yml @@ -0,0 +1,39 @@ +--- +- name: "Include grafana installation tasks" + ansible.builtin.include_tasks: install.yml + +- name: "Wait for the Grafana server to become available" + ansible.builtin.wait_for: + host: "127.0.0.1" + port: "{{ grafana_port }}" + state: started + delay: 10 + +- name: "Include user creation tasks" + when: users is defined + loop: "{{ users }}" + loop_control: + loop_var: user + ansible.builtin.include_tasks: user.yml + +- name: "Configure custom dashboards" + when: dashboard_source_path is defined + ansible.builtin.include_tasks: dashboards.yml + +- name: "Configure public dashboards" + when: public_dashboards is defined + loop: "{{ public_dashboards }}" + loop_control: + loop_var: public_dashboard + ansible.builtin.include_tasks: import_pub_dashboard.yml + +- name: "Configure plugins" + when: plugins is defined + loop: "{{ plugins }}" + loop_control: + loop_var: plugin + ansible.builtin.include_tasks: plugins.yml + +- name: "Configure datasources" + when: datasource_source_path is defined + ansible.builtin.include_tasks: datasources.yml diff --git a/roles/grafana/tasks/plugins.yml b/roles/grafana/tasks/plugins.yml new file mode 100644 index 0000000..4572019 --- /dev/null +++ b/roles/grafana/tasks/plugins.yml @@ -0,0 +1,16 @@ +--- +- name: "Create plugin directory" + ansible.builtin.file: + path: "{{ plugins_dir }}" + state: directory + owner: "{{ grafana_user }}" + group: "{{ grafana_group }}" + mode: "0755" + +- name: "Install Grafana plugins" + community.grafana.grafana_plugin: + name: "{{ plugin.name }}" + version: "{{ plugin.version }}" + grafana_plugins_dir: "{{ plugins_dir }}" + state: "{{ plugin.state | default('present') }}" + notify: "(Re)start and enable Grafana" diff --git a/roles/grafana/tasks/user.yml b/roles/grafana/tasks/user.yml new file mode 100644 index 0000000..1998fc9 --- /dev/null +++ b/roles/grafana/tasks/user.yml @@ -0,0 +1,19 @@ +--- + +- name: "Reset default admin password" + ansible.builtin.command: > + grafana-cli admin reset-admin-password "{{ admin_api_password }}" + no_log: true + changed_when: false + +- name: "Create | update a Grafana user" + community.grafana.grafana_user: + url: "http://127.0.0.1:{{ grafana_port }}" + url_username: "{{ admin_api_username }}" + url_password: "{{ admin_api_password }}" + name: "{{ user.name }}" + email: "{{ user.user_email }}" + login: "{{ user.user_login }}" + password: "{{ user.user_password }}" + is_admin: "{{ user.is_admin | default(false) }}" + state: present diff --git a/roles/grafana/templates/grafana-server.env.j2 b/roles/grafana/templates/grafana-server.env.j2 new file mode 100644 index 0000000..ced8b6c --- /dev/null +++ b/roles/grafana/templates/grafana-server.env.j2 @@ -0,0 +1,24 @@ +GRAFANA_USER=grafana + +GRAFANA_GROUP=grafana + +GRAFANA_HOME=/usr/share/grafana + +LOG_DIR={{ log_dir }} + +DATA_DIR={{ data_dir }} + +MAX_OPEN_FILES=10000 + +CONF_DIR=/etc/grafana + +CONF_FILE=/etc/grafana/grafana.ini + +RESTART_ON_UPGRADE=true + +PLUGINS_DIR={{ plugins_dir }} + +PROVISIONING_CFG_DIR=/etc/grafana/provisioning + +# Only used on systemd systems +PID_FILE_DIR=/run/grafana \ No newline at end of file diff --git a/roles/grafana/vars/main.yml b/roles/grafana/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/grafana/vars/main.yml @@ -0,0 +1 @@ +--- From e7e8a1b8ab01959b8b1b4c021e6c8f860002ccfa Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 14:32:45 +0300 Subject: [PATCH 34/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cfc2e2d..9c2dc02 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ - [alertmanager](roles/alertmanager/README.md) - [dnsmasq](roles/dnsmasq/README.md) +- [grafana](roles/grafana/README.md) - [mount_device](roles/mount_device/README.md) - [prometheus](roles/prometheus/README.md) - [ufw](roles/ufw/README.md) diff --git a/galaxy.yml b/galaxy.yml index 02c97f7..aa819d3 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.7.0 +version: 0.8.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From c3b6c01c6d1d5dfe4381de58ec07cfbd167dc6e3 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 16:10:28 +0300 Subject: [PATCH 35/96] fix linter errors --- roles/grafana/README.md | 16 ++-- roles/grafana/defaults/main.yml | 10 +-- roles/grafana/meta/main.yml | 4 +- roles/grafana/molecule/default/converge.yml | 6 +- .../datasources/test_datasource_loki.yaml | 2 +- .../test_datasource_prometheus.yaml | 78 +++++++++---------- roles/grafana/molecule/default/verify.yml | 2 +- roles/grafana/tasks/dashboards.yml | 6 +- roles/grafana/tasks/datasources.yml | 4 +- roles/grafana/tasks/install.yml | 10 +-- roles/grafana/tasks/plugins.yml | 4 +- roles/grafana/templates/grafana-server.env.j2 | 6 +- 12 files changed, 74 insertions(+), 74 deletions(-) diff --git a/roles/grafana/README.md b/roles/grafana/README.md index 5181eb8..a700863 100644 --- a/roles/grafana/README.md +++ b/roles/grafana/README.md @@ -14,7 +14,7 @@ This role installs and configures Grafana from a binary distribution. It also: - Allows admin password changes Supports user creation - Grafana service requires an environment file at startup, where you can set custom paths for logs (`log_dir`), data (`data_dir`), and plugins (`plugins_dir`). This allows flexible control over Grafana's data storage. + Grafana service requires an environment file at startup, where you can set custom paths for logs (`grafana_log_dir`), data (`grafana_data_dir`), and plugins (`grafana_plugins_dir`). This allows flexible control over Grafana's data storage. Requirements ------------ @@ -31,11 +31,11 @@ grafana_version: 11.5.0 # version grafana_port: 3000 # port # Directory paths -dashboard_dir: "/tmp/grafana/dashboards" # where to copy dashboards from source -plugins_dir: "/var/lib/grafana/plugins" # where to store plugins on target -datasource_dir: "/etc/grafana/provisioning/datasources" # where to store data sources on target -log_dir: "/var/log/grafana" # where to write logs -data_dir: "/var/lib/grafana" # where to store Grafana DB +grafana_dashboard_dir: "/tmp/grafana/dashboards" # where to copy dashboards from source +grafana_plugins_dir: "/var/lib/grafana/plugins" # where to store plugins on target +grafana_datasource_dir: "/etc/grafana/provisioning/datasources" # where to store data sources on target +grafana_log_dir: "/var/log/grafana" # where to write logs +grafana_data_dir: "/var/lib/grafana" # where to store Grafana DB # Optional configurations grafana_users: [] # array of user names, passwords, and statuses @@ -91,8 +91,8 @@ Example Playbook public_dashboards: "{{ grafana_public_dashboards }}" dashboard_source_path: "molecule/default/dashboards" datasource_source_path: "molecule/default/datasources" - log_dir: "/opt/grafana/data" - data_dir: "/opt/grafana/lib" + grafana_log_dir: "/opt/grafana/data" + grafana_data_dir: "/opt/grafana/lib" ``` diff --git a/roles/grafana/defaults/main.yml b/roles/grafana/defaults/main.yml index aa32a25..535854f 100644 --- a/roles/grafana/defaults/main.yml +++ b/roles/grafana/defaults/main.yml @@ -3,8 +3,8 @@ grafana_user: "grafana" grafana_group: "grafana" grafana_version: 11.5.0 grafana_port: 3000 -dashboard_dir: "/tmp/grafana/dashboards" -plugins_dir: "/var/lib/grafana/plugins" -datasource_dir: "/etc/grafana/provisioning/datasources" -log_dir: "/var/log/grafana" -data_dir: "/var/lib/grafana" +grafana_dashboard_dir: "/tmp/grafana/dashboards" +grafana_plugins_dir: "/var/lib/grafana/plugins" +grafana_datasource_dir: "/etc/grafana/provisioning/datasources" +grafana_log_dir: "/var/log/grafana" +grafana_data_dir: "/var/lib/grafana" diff --git a/roles/grafana/meta/main.yml b/roles/grafana/meta/main.yml index 7d3782f..cec0dd3 100644 --- a/roles/grafana/meta/main.yml +++ b/roles/grafana/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/grafana/molecule/default/converge.yml b/roles/grafana/molecule/default/converge.yml index 136f248..a73b0da 100644 --- a/roles/grafana/molecule/default/converge.yml +++ b/roles/grafana/molecule/default/converge.yml @@ -31,7 +31,7 @@ roles: # kics-scan ignore-block - - role: genlab.grafana + - role: genlab.common.grafana grafana_version: 11.5.0 admin_api_username: "{{ grafana.admin_api_username }}" admin_api_password: "{{ grafana.admin_api_password }}" @@ -40,5 +40,5 @@ public_dashboards: "{{ grafana_public_dashboards }}" dashboard_source_path: "molecule/default/dashboards" datasource_source_path: "molecule/default/datasources" - log_dir: "/opt/grafana/data" - data_dir: "/opt/grafana/lib" + grafana_log_dir: "/opt/grafana/data" + grafana_data_dir: "/opt/grafana/lib" diff --git a/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml b/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml index 335bacc..9c3ede0 100644 --- a/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml +++ b/roles/grafana/molecule/default/datasources/test_datasource_loki.yaml @@ -13,4 +13,4 @@ datasources: secureJsonData: httpHeaderValue1: "Bearer your_token_here" version: 1 - editable: false \ No newline at end of file + editable: false diff --git a/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml b/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml index 5e334f1..56ec0c6 100644 --- a/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml +++ b/roles/grafana/molecule/default/datasources/test_datasource_prometheus.yaml @@ -9,42 +9,42 @@ apiVersion: 1 # list of datasources to insert/update depending # whats available in the database datasources: - # name of the datasource. Required -- name: Prometheus - # datasource type. Required - type: prometheus - # access mode. direct or proxy. Required - access: proxy - # org id. will default to orgId 1 if not specified - orgId: 1 - # url - url: http://localhost:9090 - # database password, if used - password: - # database user, if used - user: - # database name, if used - database: - # enable/disable basic auth - basicAuth: false - # basic auth username, if used - basicAuthUser: - # basic auth password, if used - basicAuthPassword: - # enable/disable with credentials headers - withCredentials: - # mark as default datasource. Max one per org - isDefault: true - # fields that will be converted to json and stored in json_data - jsonData: - graphiteVersion: "1.1" - tlsAuth: false - tlsAuthWithCACert: false - # json object of data that will be encrypted. - secureJsonData: - tlsCACert: "..." - tlsClientCert: "..." - tlsClientKey: "..." - version: 1 - # allow users to edit datasources from the UI. - editable: true \ No newline at end of file + # name of the datasource. Required + - name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://localhost:9090 + # database password, if used + password: + # database user, if used + user: + # database name, if used + database: + # enable/disable basic auth + basicAuth: false + # basic auth username, if used + basicAuthUser: + # basic auth password, if used + basicAuthPassword: + # enable/disable with credentials headers + withCredentials: + # mark as default datasource. Max one per org + isDefault: true + # fields that will be converted to json and stored in json_data + jsonData: + graphiteVersion: "1.1" + tlsAuth: false + tlsAuthWithCACert: false + # json object of data that will be encrypted. + secureJsonData: + tlsCACert: "..." + tlsClientCert: "..." + tlsClientKey: "..." + version: 1 + # allow users to edit datasources from the UI. + editable: true diff --git a/roles/grafana/molecule/default/verify.yml b/roles/grafana/molecule/default/verify.yml index 41c2100..fd0a907 100644 --- a/roles/grafana/molecule/default/verify.yml +++ b/roles/grafana/molecule/default/verify.yml @@ -11,7 +11,7 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Check if Grafana is installed" changed_when: false diff --git a/roles/grafana/tasks/dashboards.yml b/roles/grafana/tasks/dashboards.yml index 2c1a7ff..79c0604 100644 --- a/roles/grafana/tasks/dashboards.yml +++ b/roles/grafana/tasks/dashboards.yml @@ -1,7 +1,7 @@ --- - name: "Create dashboard directory" ansible.builtin.file: - path: "{{ dashboard_dir }}" + path: "{{ grafana_dashboard_dir }}" state: directory owner: "{{ grafana_user }}" group: "{{ grafana_group }}" @@ -10,7 +10,7 @@ - name: "Copy dashboard files from source to target" ansible.builtin.copy: src: "{{ item }}" - dest: "{{ dashboard_dir }}/{{ item | basename }}" + dest: "{{ grafana_dashboard_dir }}/{{ item | basename }}" mode: "0644" owner: "{{ grafana_user }}" group: "{{ grafana_group }}" @@ -25,7 +25,7 @@ state: present commit_message: Updated by ansible overwrite: false - path: "{{ dashboard_dir }}/{{ item | basename }}" + path: "{{ grafana_dashboard_dir }}/{{ item | basename }}" with_fileglob: - "{{ dashboard_source_path }}/*.json" diff --git a/roles/grafana/tasks/datasources.yml b/roles/grafana/tasks/datasources.yml index c5a2efe..94025c0 100644 --- a/roles/grafana/tasks/datasources.yml +++ b/roles/grafana/tasks/datasources.yml @@ -1,7 +1,7 @@ --- - name: "Create datasource directory" ansible.builtin.file: - path: "{{ datasource_dir }}" + path: "{{ grafana_datasource_dir }}" state: directory owner: "{{ grafana_user }}" group: "{{ grafana_group }}" @@ -10,7 +10,7 @@ - name: "Configure | provision datasources for Grafana" ansible.builtin.copy: src: "{{ datasource_source_path }}/{{ item | basename }}" - dest: "{{ datasource_dir }}/{{ item | basename }}" + dest: "{{ grafana_datasource_dir }}/{{ item | basename }}" owner: "{{ grafana_user }}" group: "{{ grafana_group }}" mode: "0660" diff --git a/roles/grafana/tasks/install.yml b/roles/grafana/tasks/install.yml index e79e378..0f7d33d 100644 --- a/roles/grafana/tasks/install.yml +++ b/roles/grafana/tasks/install.yml @@ -37,9 +37,9 @@ group: "{{ grafana_group }}" mode: '0775' with_items: - - "{{ log_dir }}" - - "{{ data_dir }}" - - "{{ plugins_dir }}" + - "{{ grafana_log_dir }}" + - "{{ grafana_data_dir }}" + - "{{ grafana_plugins_dir }}" - name: "Download Grafana DEB package" ansible.builtin.get_url: @@ -48,12 +48,12 @@ owner: "{{ grafana_user }}" group: "{{ grafana_group }}" mode: "0644" - register: package_tmp + register: grafana_package_tmp - name: "Install DEB package" notify: "(Re)start and enable Grafana" ansible.builtin.apt: - deb: "{{ package_tmp.dest }}" + deb: "{{ grafana_package_tmp.dest }}" state: present update_cache: true diff --git a/roles/grafana/tasks/plugins.yml b/roles/grafana/tasks/plugins.yml index 4572019..96af00c 100644 --- a/roles/grafana/tasks/plugins.yml +++ b/roles/grafana/tasks/plugins.yml @@ -1,7 +1,7 @@ --- - name: "Create plugin directory" ansible.builtin.file: - path: "{{ plugins_dir }}" + path: "{{ grafana_plugins_dir }}" state: directory owner: "{{ grafana_user }}" group: "{{ grafana_group }}" @@ -11,6 +11,6 @@ community.grafana.grafana_plugin: name: "{{ plugin.name }}" version: "{{ plugin.version }}" - grafana_plugins_dir: "{{ plugins_dir }}" + grafana_plugins_dir: "{{ grafana_plugins_dir }}" state: "{{ plugin.state | default('present') }}" notify: "(Re)start and enable Grafana" diff --git a/roles/grafana/templates/grafana-server.env.j2 b/roles/grafana/templates/grafana-server.env.j2 index ced8b6c..0abd646 100644 --- a/roles/grafana/templates/grafana-server.env.j2 +++ b/roles/grafana/templates/grafana-server.env.j2 @@ -4,9 +4,9 @@ GRAFANA_GROUP=grafana GRAFANA_HOME=/usr/share/grafana -LOG_DIR={{ log_dir }} +LOG_DIR={{ grafana_log_dir }} -DATA_DIR={{ data_dir }} +DATA_DIR={{ grafana_data_dir }} MAX_OPEN_FILES=10000 @@ -16,7 +16,7 @@ CONF_FILE=/etc/grafana/grafana.ini RESTART_ON_UPGRADE=true -PLUGINS_DIR={{ plugins_dir }} +PLUGINS_DIR={{ grafana_plugins_dir }} PROVISIONING_CFG_DIR=/etc/grafana/provisioning From 04f73a6f96cabcab52b7f3e767bed977e6407e71 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 16:12:08 +0300 Subject: [PATCH 36/96] remove garbage --- roles/grafana/.ansible-lint | 19 ------------------- roles/grafana/.yamllint | 8 -------- roles/grafana/ansible-navigator.log | 0 roles/grafana/conda.dev.yml | 11 ----------- roles/grafana/conda.prod.yml | 9 --------- roles/grafana/renovate.json | 6 ------ roles/grafana/requirements.ci.txt | 6 ------ roles/grafana/requirements.txt | 1 - roles/grafana/requirements.yml | 4 ---- 9 files changed, 64 deletions(-) delete mode 100644 roles/grafana/.ansible-lint delete mode 100644 roles/grafana/.yamllint delete mode 100644 roles/grafana/ansible-navigator.log delete mode 100644 roles/grafana/conda.dev.yml delete mode 100644 roles/grafana/conda.prod.yml delete mode 100644 roles/grafana/renovate.json delete mode 100644 roles/grafana/requirements.ci.txt delete mode 100644 roles/grafana/requirements.txt delete mode 100644 roles/grafana/requirements.yml diff --git a/roles/grafana/.ansible-lint b/roles/grafana/.ansible-lint deleted file mode 100644 index 23e8eb6..0000000 --- a/roles/grafana/.ansible-lint +++ /dev/null @@ -1,19 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ - - molecule/default/secrets/ - - molecule/default/datasources/ - - molecule/default/dashboards/ diff --git a/roles/grafana/.yamllint b/roles/grafana/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/grafana/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/grafana/ansible-navigator.log b/roles/grafana/ansible-navigator.log deleted file mode 100644 index e69de29..0000000 diff --git a/roles/grafana/conda.dev.yml b/roles/grafana/conda.dev.yml deleted file mode 100644 index fb974b7..0000000 --- a/roles/grafana/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/grafana/conda.prod.yml b/roles/grafana/conda.prod.yml deleted file mode 100644 index d6d12d6..0000000 --- a/roles/grafana/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/grafana/renovate.json b/roles/grafana/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/grafana/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/grafana/requirements.ci.txt b/roles/grafana/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/grafana/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/grafana/requirements.txt b/roles/grafana/requirements.txt deleted file mode 100644 index f3b3882..0000000 --- a/roles/grafana/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.2.0 diff --git a/roles/grafana/requirements.yml b/roles/grafana/requirements.yml deleted file mode 100644 index 2e509b8..0000000 --- a/roles/grafana/requirements.yml +++ /dev/null @@ -1,4 +0,0 @@ -# requirements file ---- -collections: - - name: community.grafana From 35606ab1b3d8414690629094603c628edf32834a Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 16:18:53 +0300 Subject: [PATCH 37/96] add community.grafana collection install --- .github/workflows/molecule.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml index 97885ee..5fcb13d 100644 --- a/.github/workflows/molecule.yml +++ b/.github/workflows/molecule.yml @@ -82,6 +82,10 @@ jobs: run: ansible-galaxy collection install community.general shell: micromamba-shell {0} + - name: "Install community.grafana collection" + run: ansible-galaxy collection install community.grafana + shell: micromamba-shell {0} + - name: "Run Molecule tests" if: ${{ matrix.role != '__no_role__' }} working-directory: ${{ matrix.role }} From 476457888eed4db5327705324bd37b02c21d48cb Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 16:26:04 +0300 Subject: [PATCH 38/96] add ipmi_exporter role --- roles/ipmi_exporter/.ansible-lint | 16 +++++ roles/ipmi_exporter/.yamllint | 8 +++ roles/ipmi_exporter/README.md | 61 +++++++++++++++++++ roles/ipmi_exporter/conda.dev.yml | 11 ++++ roles/ipmi_exporter/conda.prod.yml | 9 +++ roles/ipmi_exporter/defaults/main.yml | 8 +++ roles/ipmi_exporter/handlers/main.yml | 7 +++ roles/ipmi_exporter/meta/main.yml | 17 ++++++ .../molecule/default/converge.yml | 6 ++ .../molecule/default/ipmi_local.conf | 12 ++++ .../molecule/default/molecule.yml | 27 ++++++++ .../ipmi_exporter/molecule/default/verify.yml | 37 +++++++++++ roles/ipmi_exporter/renovate.json | 6 ++ roles/ipmi_exporter/requirements.ci.txt | 6 ++ roles/ipmi_exporter/requirements.txt | 1 + roles/ipmi_exporter/requirements.yml | 3 + roles/ipmi_exporter/tasks/configure.yml | 20 ++++++ roles/ipmi_exporter/tasks/install.yml | 51 ++++++++++++++++ roles/ipmi_exporter/tasks/main.yml | 7 +++ .../templates/ipmi_exporter.service.j2 | 38 ++++++++++++ roles/ipmi_exporter/vars/main.yml | 1 + 21 files changed, 352 insertions(+) create mode 100644 roles/ipmi_exporter/.ansible-lint create mode 100644 roles/ipmi_exporter/.yamllint create mode 100644 roles/ipmi_exporter/README.md create mode 100644 roles/ipmi_exporter/conda.dev.yml create mode 100644 roles/ipmi_exporter/conda.prod.yml create mode 100644 roles/ipmi_exporter/defaults/main.yml create mode 100644 roles/ipmi_exporter/handlers/main.yml create mode 100644 roles/ipmi_exporter/meta/main.yml create mode 100644 roles/ipmi_exporter/molecule/default/converge.yml create mode 100644 roles/ipmi_exporter/molecule/default/ipmi_local.conf create mode 100644 roles/ipmi_exporter/molecule/default/molecule.yml create mode 100644 roles/ipmi_exporter/molecule/default/verify.yml create mode 100644 roles/ipmi_exporter/renovate.json create mode 100644 roles/ipmi_exporter/requirements.ci.txt create mode 100644 roles/ipmi_exporter/requirements.txt create mode 100644 roles/ipmi_exporter/requirements.yml create mode 100644 roles/ipmi_exporter/tasks/configure.yml create mode 100644 roles/ipmi_exporter/tasks/install.yml create mode 100644 roles/ipmi_exporter/tasks/main.yml create mode 100644 roles/ipmi_exporter/templates/ipmi_exporter.service.j2 create mode 100644 roles/ipmi_exporter/vars/main.yml diff --git a/roles/ipmi_exporter/.ansible-lint b/roles/ipmi_exporter/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/ipmi_exporter/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/ipmi_exporter/.yamllint b/roles/ipmi_exporter/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/ipmi_exporter/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/ipmi_exporter/README.md b/roles/ipmi_exporter/README.md new file mode 100644 index 0000000..06d3620 --- /dev/null +++ b/roles/ipmi_exporter/README.md @@ -0,0 +1,61 @@ +genlab.ipmi_exporter +========= + +This Ansible role installs ipmi_exporter on target host. This is a Prometheus exporter for Intelligent Platform Management Interface [metrics](https://github.com/prometheus-community/ipmi_exporter/blob/master/docs/metrics.md) + +Requirements +------------ + +By default, the exporter relies on tools from the FreeIPMI suite for the actual IPMI implementation. + +Role Variables +-------------- +Configuration files must have names ```web_conf.yaml``` and ```ipmi_local.conf```. If ipmi_exp_source_dir is specified, the role searches for ```web_conf.yaml``` and ```ipmi_local.conf``` in that directory and copy to target host in ```ipmi_exp_config_dir```. If the source directory is not specified, the role skips this step. In ipmi_local.conf user can describe what modules to use for metric collection. + +ipmi_up{collector=""} is 1 if the data for this collector could successfully be retrieved from the remote host, 0 otherwise. The following collectors are available and can be enabled or disabled in the config: +- ipmi: collects IPMI sensor data. If it fails, sensor metrics (see below) will not be available +- dcmi: collects DCMI data, currently only power consumption. If it fails, power consumption metrics (see below) will not be available +- bmc: collects BMC details. If it fails, BMC info metrics (see below) will not be available +- bmc-watchdog: collects status of the watchdog. If it fails, BMC watchdog metrics (see below) will not be available +- chassis: collects the current chassis power state (on/off). If it fails, the chassis power state metric (see below) will not be available +- sel: collects system event log (SEL) details. If it fails, SEL metrics (see below) will not be available +- sel-events: collects metrics for user-defined events in system event log (SEL). If it fails, SEL entries metrics (see below) will not be available +- sm-lan-mode: collects the "LAN mode" setting in the current BMC config. If it fails, the LAN mode metric (see below) will not be available + +```yaml +ipmi_exp_version: 1.10.1 +ipmi_exp_dir: "/etc/exporters" +ipmi_exp_config_dir: "/etc/exporters/config" +ipmi_exp_args: "" # --[no-]native-ipmi Use native IPMI implementation instead of FreeIPMI (EXPERIMENTAL) + # --[no-]web.systemd-socket Use systemd socket activation listeners instead of port listeners (Linux only). +ipmi_exp_log_level: "info" # Only log messages with the given severity or above. One of: [debug, info, warn, error] +ipmi_exp_log_format: "logfmt" # Output format of log messages. One of: [logfmt, json] +ipmi_exp_web_listen_address: "localhost:9290" # Addresses on which to expose metrics and web interface. Repeatable for multiple addresses. Examples: `:9100` or `[::1]:9100` for http, vsock://:9100` for vsock +ipmi_exp_source_dir: ipmi_local.conf # Path to configuration file. See: https://github.com/prometheus-community/ipmi_exporter/blob/master/docs/configuration.md +ipmi_exp_web_source_dir: web_conf.yaml # Path to configuration file that can enable TLS or authentication. See: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md +``` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml + roles: + - role: genlab.ipmi_exporter + ipmi_exp_version: "1.10.1" + ipmi_exp_source_dir: "molecule/default/" +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/ipmi_exporter/conda.dev.yml b/roles/ipmi_exporter/conda.dev.yml new file mode 100644 index 0000000..fb974b7 --- /dev/null +++ b/roles/ipmi_exporter/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/ipmi_exporter/conda.prod.yml b/roles/ipmi_exporter/conda.prod.yml new file mode 100644 index 0000000..d6d12d6 --- /dev/null +++ b/roles/ipmi_exporter/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/ipmi_exporter/defaults/main.yml b/roles/ipmi_exporter/defaults/main.yml new file mode 100644 index 0000000..a019389 --- /dev/null +++ b/roles/ipmi_exporter/defaults/main.yml @@ -0,0 +1,8 @@ +--- +ipmi_exp_version: 1.10.1 +ipmi_exp_dir: "/etc/exporters" +ipmi_exp_config_dir: "/etc/exporters/config" +ipmi_exp_log_level: "info" +ipmi_exp_log_format: "logfmt" +ipmi_exp_web_listen_address: "localhost:9290" +ipmi_exp_args: "" diff --git a/roles/ipmi_exporter/handlers/main.yml b/roles/ipmi_exporter/handlers/main.yml new file mode 100644 index 0000000..57f5d52 --- /dev/null +++ b/roles/ipmi_exporter/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "(Re)start and enable ipmi_exporter" + ansible.builtin.systemd_service: + name: ipmi_exporter.service + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/ipmi_exporter/meta/main.yml b/roles/ipmi_exporter/meta/main.yml new file mode 100644 index 0000000..416e73c --- /dev/null +++ b/roles/ipmi_exporter/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "ipmi_exporter" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/ipmi_exporter/molecule/default/converge.yml b/roles/ipmi_exporter/molecule/default/converge.yml new file mode 100644 index 0000000..abf2fc6 --- /dev/null +++ b/roles/ipmi_exporter/molecule/default/converge.yml @@ -0,0 +1,6 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.ipmi_exporter + ipmi_exp_source_dir: "molecule/default/" diff --git a/roles/ipmi_exporter/molecule/default/ipmi_local.conf b/roles/ipmi_exporter/molecule/default/ipmi_local.conf new file mode 100644 index 0000000..db488bf --- /dev/null +++ b/roles/ipmi_exporter/molecule/default/ipmi_local.conf @@ -0,0 +1,12 @@ +modules: + default: + # Available collectors are bmc, bmc-watchdog, ipmi, chassis, dcmi, sel, sel-events and sm-lan-mode + collectors: + - bmc + - bmc-watchdog + - ipmi + - dcmi + - chassis + - sel + - sel-events + - sm-lan-mode \ No newline at end of file diff --git a/roles/ipmi_exporter/molecule/default/molecule.yml b/roles/ipmi_exporter/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/ipmi_exporter/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/ipmi_exporter/molecule/default/verify.yml b/roles/ipmi_exporter/molecule/default/verify.yml new file mode 100644 index 0000000..ffdfe90 --- /dev/null +++ b/roles/ipmi_exporter/molecule/default/verify.yml @@ -0,0 +1,37 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Check if ipmi_exporter is installed" + changed_when: false + ansible.builtin.command: "ipmi_exporter --version" + register: ipmi_exporter_installed_version + + - name: "Check ipmi_exporter version" + ansible.builtin.assert: + that: "ipmi_exporter_installed_version.stdout is regex('{{ ipmi_exp_version }}')" + success_msg: "ipmi_exporter version {{ ipmi_exp_version }} is installed and working" + fail_msg: "ipmi_exporter version {{ ipmi_exp_version }} is not installed or not working correctly" + + # kics-scan ignore-block + - name: Check if /metrics endpoint is reachable + ansible.builtin.uri: + url: "http://{{ ipmi_exp_web_listen_address }}/metrics" + return_content: true + status_code: 200 + timeout: 60 + register: metrics_check + + - name: "Fail if /metrics doesn't contain ipmi_exporter_build_info line" + ansible.builtin.assert: + that: "'ipmi_exporter_build_info' in metrics_check.content" + fail_msg: "ipmi_exporter /metrics endpoint doesn't contain ipmi_exporter_build_info line!" + success_msg: "ipmi_exporter /metrics endpoint contains ipmi_exporter_build_info line!" diff --git a/roles/ipmi_exporter/renovate.json b/roles/ipmi_exporter/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/ipmi_exporter/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/ipmi_exporter/requirements.ci.txt b/roles/ipmi_exporter/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/ipmi_exporter/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/ipmi_exporter/requirements.txt b/roles/ipmi_exporter/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/ipmi_exporter/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/ipmi_exporter/requirements.yml b/roles/ipmi_exporter/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/ipmi_exporter/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/ipmi_exporter/tasks/configure.yml b/roles/ipmi_exporter/tasks/configure.yml new file mode 100644 index 0000000..3851e6f --- /dev/null +++ b/roles/ipmi_exporter/tasks/configure.yml @@ -0,0 +1,20 @@ +--- +- name: "Upload ipmi_exporter local configure file" + notify: "(Re)start and enable ipmi_exporter" + when: ipmi_exp_source_dir is defined and ipmi_exp_source_dir | length > 0 + ansible.builtin.template: + src: "{{ ipmi_exp_source_dir }}/ipmi_local.conf" + dest: "{{ ipmi_exp_config_dir }}/ipmi_local.conf" + owner: root + group: root + mode: '0640' + +- name: "Upload ipmi_exporter web configure file" + notify: "(Re)start and enable ipmi_exporter" + when: ipmi_exp_web_source_dir is defined and ipmi_exp_web_source_dir | length > 0 + ansible.builtin.template: + src: "{{ ipmi_exp_web_source_dir }}/web_conf.yaml" + dest: "{{ ipmi_exp_config_dir }}/web_conf.yaml" + owner: root + group: root + mode: '0640' diff --git a/roles/ipmi_exporter/tasks/install.yml b/roles/ipmi_exporter/tasks/install.yml new file mode 100644 index 0000000..1466d76 --- /dev/null +++ b/roles/ipmi_exporter/tasks/install.yml @@ -0,0 +1,51 @@ +--- +- name: "Install ipmi_exporter from binary" + block: + - name: "Check ipmi_exporter version" + changed_when: false + ansible.builtin.command: + cmd: "ipmi_exporter --version" + register: ipmi_exporter_ver + + - name: "Assert version correctness" + ansible.builtin.assert: + that: "ipmi_exporter_ver.stdout is regex('{{ ipmi_exp_version }}')" + success_msg: "ipmi_exporter version {{ ipmi_exp_version }} is installed and working" + fail_msg: "ipmi_exporter version {{ ipmi_exp_version }} is not installed or not working correctly" + + rescue: + - name: "Create ipmi_exporter directories" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0755" + with_items: + - "{{ ipmi_exp_dir }}" + - "{{ ipmi_exp_config_dir }}" + + - name: "Unarchive ipmi_exporter tar file" + notify: "(Re)start and enable ipmi_exporter" + ansible.builtin.unarchive: + src: "https://github.com/prometheus-community/ipmi_exporter/releases/\ + download/v{{ ipmi_exp_version }}/ipmi_exporter-{{ ipmi_exp_version }}.linux-amd64.tar.gz" + dest: "{{ ipmi_exp_dir }}" + remote_src: true + + - name: "Move ipmi_exporter binary" + ansible.builtin.copy: + src: "{{ ipmi_exp_dir }}/ipmi_exporter-{{ ipmi_exp_version }}.linux-amd64/ipmi_exporter" + dest: "/usr/local/bin/ipmi_exporter" + mode: "0755" + owner: root + group: root + remote_src: true + +- name: Create systemd service file + ansible.builtin.template: + src: ipmi_exporter.service.j2 + dest: /etc/systemd/system/ipmi_exporter.service + owner: root + group: root + mode: '0644' diff --git a/roles/ipmi_exporter/tasks/main.yml b/roles/ipmi_exporter/tasks/main.yml new file mode 100644 index 0000000..b926b2c --- /dev/null +++ b/roles/ipmi_exporter/tasks/main.yml @@ -0,0 +1,7 @@ +--- + +- name: "Include installation tasks" + ansible.builtin.include_tasks: "install.yml" + +- name: "Including configuration tasks" + ansible.builtin.include_tasks: "configure.yml" diff --git a/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 b/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 new file mode 100644 index 0000000..bf61227 --- /dev/null +++ b/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 @@ -0,0 +1,38 @@ +[Unit] +Description=IPMI exporter +Documentation=https://github.com/prometheus-community/ipmi_exporter +After=network.target +StartLimitIntervalSec=120 +StartLimitBurst=5 + +[Service] +Type=simple +ExecStart=/usr/local/bin/ipmi_exporter \ + --web.listen-address={{ ipmi_exp_web_listen_address }} \ + --log.level={{ ipmi_exp_log_level }} \ + --log.format={{ ipmi_exp_log_format }} \ +{% if ipmi_exp_args is defined and ipmi_exp_args | length > 0 %} + {{ ipmi_exp_args }} \ +{% endif %} +{% if ipmi_exp_source_dir is defined and ipmi_exp_source_dir | length > 0 %} + --config.file={{ ipmi_exp_config_dir }}/ipmi_local.conf \ +{% endif %} +{% if ipmi_exp_web_source_dir is defined and ipmi_exp_web_source_dir | length > 0 %} + --web.config.file={{ ipmi_exp_config_dir }}/web_conf.yaml +{% endif %} + +SyslogIdentifier=ipmi_exporter +Restart=on-failure +RestartSec=5 + +ProtectHome=yes +NoNewPrivileges=yes +ProtectSystem=strict +ProtectControlGroups=true +ProtectKernelModules=true +ProtectKernelTunables=yes +PrivateTmp=true +ProtectSystem=full + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/ipmi_exporter/vars/main.yml b/roles/ipmi_exporter/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/ipmi_exporter/vars/main.yml @@ -0,0 +1 @@ +--- From c4d5fe1607c038f078799608e88292717d69033b Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 16:28:10 +0300 Subject: [PATCH 39/96] change meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c2dc02..5bd9d0d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - [alertmanager](roles/alertmanager/README.md) - [dnsmasq](roles/dnsmasq/README.md) - [grafana](roles/grafana/README.md) +- [ipmi_exporter](roles/ipmi_exporter/README.md) - [mount_device](roles/mount_device/README.md) - [prometheus](roles/prometheus/README.md) - [ufw](roles/ufw/README.md) diff --git a/galaxy.yml b/galaxy.yml index aa819d3..c68bddb 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.8.0 +version: 0.9.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 65925bea6f9e8874da2be9a1beed36aadf1e2932 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 17:10:49 +0300 Subject: [PATCH 40/96] fix linter errors --- roles/ipmi_exporter/defaults/main.yml | 14 +++++++------- roles/ipmi_exporter/meta/main.yml | 4 ++-- .../molecule/default/converge.yml | 2 +- .../ipmi_exporter/molecule/default/verify.yml | 14 +++++++------- roles/ipmi_exporter/tasks/configure.yml | 4 ++-- roles/ipmi_exporter/tasks/install.yml | 16 ++++++++-------- .../templates/ipmi_exporter.service.j2 | 18 +++++++++--------- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/roles/ipmi_exporter/defaults/main.yml b/roles/ipmi_exporter/defaults/main.yml index a019389..c67b875 100644 --- a/roles/ipmi_exporter/defaults/main.yml +++ b/roles/ipmi_exporter/defaults/main.yml @@ -1,8 +1,8 @@ --- -ipmi_exp_version: 1.10.1 -ipmi_exp_dir: "/etc/exporters" -ipmi_exp_config_dir: "/etc/exporters/config" -ipmi_exp_log_level: "info" -ipmi_exp_log_format: "logfmt" -ipmi_exp_web_listen_address: "localhost:9290" -ipmi_exp_args: "" +ipmi_exporter_version: 1.10.1 +ipmi_exporter_dir: "/etc/exporters" +ipmi_exporter_config_dir: "/etc/exporters/config" +ipmi_exporter_log_level: "info" +ipmi_exporter_log_format: "logfmt" +ipmi_exporter_web_listen_address: "localhost:9290" +ipmi_exporter_args: "" diff --git a/roles/ipmi_exporter/meta/main.yml b/roles/ipmi_exporter/meta/main.yml index 416e73c..3c37e0c 100644 --- a/roles/ipmi_exporter/meta/main.yml +++ b/roles/ipmi_exporter/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/ipmi_exporter/molecule/default/converge.yml b/roles/ipmi_exporter/molecule/default/converge.yml index abf2fc6..40122c5 100644 --- a/roles/ipmi_exporter/molecule/default/converge.yml +++ b/roles/ipmi_exporter/molecule/default/converge.yml @@ -2,5 +2,5 @@ - name: Converge hosts: all roles: - - role: genlab.ipmi_exporter + - role: genlab.common.ipmi_exporter ipmi_exp_source_dir: "molecule/default/" diff --git a/roles/ipmi_exporter/molecule/default/verify.yml b/roles/ipmi_exporter/molecule/default/verify.yml index ffdfe90..95aeb62 100644 --- a/roles/ipmi_exporter/molecule/default/verify.yml +++ b/roles/ipmi_exporter/molecule/default/verify.yml @@ -8,7 +8,7 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Check if ipmi_exporter is installed" changed_when: false @@ -17,21 +17,21 @@ - name: "Check ipmi_exporter version" ansible.builtin.assert: - that: "ipmi_exporter_installed_version.stdout is regex('{{ ipmi_exp_version }}')" - success_msg: "ipmi_exporter version {{ ipmi_exp_version }} is installed and working" - fail_msg: "ipmi_exporter version {{ ipmi_exp_version }} is not installed or not working correctly" + that: "ipmi_exporter_installed_version.stdout is regex('{{ ipmi_exporter_version }}')" + success_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is installed and working" + fail_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is not installed or not working correctly" # kics-scan ignore-block - name: Check if /metrics endpoint is reachable ansible.builtin.uri: - url: "http://{{ ipmi_exp_web_listen_address }}/metrics" + url: "http://{{ ipmi_exporter_web_listen_address }}/metrics" return_content: true status_code: 200 timeout: 60 - register: metrics_check + register: ipmi_exporter_metrics_check - name: "Fail if /metrics doesn't contain ipmi_exporter_build_info line" ansible.builtin.assert: - that: "'ipmi_exporter_build_info' in metrics_check.content" + that: "'ipmi_exporter_build_info' in ipmi_exporter_metrics_check.content" fail_msg: "ipmi_exporter /metrics endpoint doesn't contain ipmi_exporter_build_info line!" success_msg: "ipmi_exporter /metrics endpoint contains ipmi_exporter_build_info line!" diff --git a/roles/ipmi_exporter/tasks/configure.yml b/roles/ipmi_exporter/tasks/configure.yml index 3851e6f..04af8f5 100644 --- a/roles/ipmi_exporter/tasks/configure.yml +++ b/roles/ipmi_exporter/tasks/configure.yml @@ -4,7 +4,7 @@ when: ipmi_exp_source_dir is defined and ipmi_exp_source_dir | length > 0 ansible.builtin.template: src: "{{ ipmi_exp_source_dir }}/ipmi_local.conf" - dest: "{{ ipmi_exp_config_dir }}/ipmi_local.conf" + dest: "{{ ipmi_exporter_config_dir }}/ipmi_local.conf" owner: root group: root mode: '0640' @@ -14,7 +14,7 @@ when: ipmi_exp_web_source_dir is defined and ipmi_exp_web_source_dir | length > 0 ansible.builtin.template: src: "{{ ipmi_exp_web_source_dir }}/web_conf.yaml" - dest: "{{ ipmi_exp_config_dir }}/web_conf.yaml" + dest: "{{ ipmi_exporter_config_dir }}/web_conf.yaml" owner: root group: root mode: '0640' diff --git a/roles/ipmi_exporter/tasks/install.yml b/roles/ipmi_exporter/tasks/install.yml index 1466d76..4d9ec03 100644 --- a/roles/ipmi_exporter/tasks/install.yml +++ b/roles/ipmi_exporter/tasks/install.yml @@ -9,9 +9,9 @@ - name: "Assert version correctness" ansible.builtin.assert: - that: "ipmi_exporter_ver.stdout is regex('{{ ipmi_exp_version }}')" - success_msg: "ipmi_exporter version {{ ipmi_exp_version }} is installed and working" - fail_msg: "ipmi_exporter version {{ ipmi_exp_version }} is not installed or not working correctly" + that: "ipmi_exporter_ver.stdout is regex('{{ ipmi_exporter_version }}')" + success_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is installed and working" + fail_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is not installed or not working correctly" rescue: - name: "Create ipmi_exporter directories" @@ -22,20 +22,20 @@ group: root mode: "0755" with_items: - - "{{ ipmi_exp_dir }}" - - "{{ ipmi_exp_config_dir }}" + - "{{ ipmi_exporter_dir }}" + - "{{ ipmi_exporter_config_dir }}" - name: "Unarchive ipmi_exporter tar file" notify: "(Re)start and enable ipmi_exporter" ansible.builtin.unarchive: src: "https://github.com/prometheus-community/ipmi_exporter/releases/\ - download/v{{ ipmi_exp_version }}/ipmi_exporter-{{ ipmi_exp_version }}.linux-amd64.tar.gz" - dest: "{{ ipmi_exp_dir }}" + download/v{{ ipmi_exporter_version }}/ipmi_exporter-{{ ipmi_exporter_version }}.linux-amd64.tar.gz" + dest: "{{ ipmi_exporter_dir }}" remote_src: true - name: "Move ipmi_exporter binary" ansible.builtin.copy: - src: "{{ ipmi_exp_dir }}/ipmi_exporter-{{ ipmi_exp_version }}.linux-amd64/ipmi_exporter" + src: "{{ ipmi_exporter_dir }}/ipmi_exporter-{{ ipmi_exporter_version }}.linux-amd64/ipmi_exporter" dest: "/usr/local/bin/ipmi_exporter" mode: "0755" owner: root diff --git a/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 b/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 index bf61227..6b41d18 100644 --- a/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 +++ b/roles/ipmi_exporter/templates/ipmi_exporter.service.j2 @@ -8,17 +8,17 @@ StartLimitBurst=5 [Service] Type=simple ExecStart=/usr/local/bin/ipmi_exporter \ - --web.listen-address={{ ipmi_exp_web_listen_address }} \ - --log.level={{ ipmi_exp_log_level }} \ - --log.format={{ ipmi_exp_log_format }} \ -{% if ipmi_exp_args is defined and ipmi_exp_args | length > 0 %} - {{ ipmi_exp_args }} \ + --web.listen-address={{ ipmi_exporter_web_listen_address }} \ + --log.level={{ ipmi_exporter_log_level }} \ + --log.format={{ ipmi_exporter_log_format }} \ +{% if ipmi_exporter_args is defined and ipmi_exporter_args | length > 0 %} + {{ ipmi_exporter_args }} \ {% endif %} -{% if ipmi_exp_source_dir is defined and ipmi_exp_source_dir | length > 0 %} - --config.file={{ ipmi_exp_config_dir }}/ipmi_local.conf \ +{% if ipmi_exporter_source_dir is defined and ipmi_exporter_source_dir | length > 0 %} + --config.file={{ ipmi_exporter_config_dir }}/ipmi_local.conf \ {% endif %} -{% if ipmi_exp_web_source_dir is defined and ipmi_exp_web_source_dir | length > 0 %} - --web.config.file={{ ipmi_exp_config_dir }}/web_conf.yaml +{% if ipmi_exporter_web_source_dir is defined and ipmi_exporter_web_source_dir | length > 0 %} + --web.config.file={{ ipmi_exporter_config_dir }}/web_conf.yaml {% endif %} SyslogIdentifier=ipmi_exporter From dad3bfa9434d840c53ee40b16c01fcc587f2a16f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 17:11:58 +0300 Subject: [PATCH 41/96] remove garbage --- roles/ipmi_exporter/.ansible-lint | 16 ---------------- roles/ipmi_exporter/.yamllint | 8 -------- roles/ipmi_exporter/conda.dev.yml | 11 ----------- roles/ipmi_exporter/conda.prod.yml | 9 --------- roles/ipmi_exporter/renovate.json | 6 ------ roles/ipmi_exporter/requirements.ci.txt | 6 ------ roles/ipmi_exporter/requirements.txt | 1 - roles/ipmi_exporter/requirements.yml | 3 --- 8 files changed, 60 deletions(-) delete mode 100644 roles/ipmi_exporter/.ansible-lint delete mode 100644 roles/ipmi_exporter/.yamllint delete mode 100644 roles/ipmi_exporter/conda.dev.yml delete mode 100644 roles/ipmi_exporter/conda.prod.yml delete mode 100644 roles/ipmi_exporter/renovate.json delete mode 100644 roles/ipmi_exporter/requirements.ci.txt delete mode 100644 roles/ipmi_exporter/requirements.txt delete mode 100644 roles/ipmi_exporter/requirements.yml diff --git a/roles/ipmi_exporter/.ansible-lint b/roles/ipmi_exporter/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/ipmi_exporter/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/ipmi_exporter/.yamllint b/roles/ipmi_exporter/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/ipmi_exporter/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/ipmi_exporter/conda.dev.yml b/roles/ipmi_exporter/conda.dev.yml deleted file mode 100644 index fb974b7..0000000 --- a/roles/ipmi_exporter/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/ipmi_exporter/conda.prod.yml b/roles/ipmi_exporter/conda.prod.yml deleted file mode 100644 index d6d12d6..0000000 --- a/roles/ipmi_exporter/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/ipmi_exporter/renovate.json b/roles/ipmi_exporter/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/ipmi_exporter/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/ipmi_exporter/requirements.ci.txt b/roles/ipmi_exporter/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/ipmi_exporter/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/ipmi_exporter/requirements.txt b/roles/ipmi_exporter/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/ipmi_exporter/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 diff --git a/roles/ipmi_exporter/requirements.yml b/roles/ipmi_exporter/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/ipmi_exporter/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From e16cc72ab2505f53098789fd02cfcf01dff89b79 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 17:22:02 +0300 Subject: [PATCH 42/96] add borgmatic role --- roles/borgmatic/.ansible-lint | 16 +++++ roles/borgmatic/.gitignore | 3 + roles/borgmatic/.yamllint | 8 +++ roles/borgmatic/README.md | 40 +++++++++++ roles/borgmatic/conda.dev.yml | 11 ++++ roles/borgmatic/conda.prod.yml | 9 +++ roles/borgmatic/defaults/main.yml | 17 +++++ roles/borgmatic/handlers/main.yml | 1 + roles/borgmatic/meta/main.yml | 17 +++++ roles/borgmatic/molecule/default/converge.yml | 48 ++++++++++++++ roles/borgmatic/molecule/default/molecule.yml | 31 +++++++++ roles/borgmatic/molecule/default/verify.yml | 48 ++++++++++++++ roles/borgmatic/renovate.json | 6 ++ roles/borgmatic/requirements.ci.txt | 6 ++ roles/borgmatic/requirements.txt | 1 + roles/borgmatic/requirements.yml | 6 ++ roles/borgmatic/tasks/config.yml | 34 ++++++++++ roles/borgmatic/tasks/handle-ssh-keys.yml | 25 +++++++ roles/borgmatic/tasks/install.yml | 66 +++++++++++++++++++ roles/borgmatic/tasks/integrations.yml | 1 + roles/borgmatic/tasks/main.yml | 15 +++++ roles/borgmatic/tasks/run.yml | 22 +++++++ roles/borgmatic/vars/main.yml | 1 + 23 files changed, 432 insertions(+) create mode 100644 roles/borgmatic/.ansible-lint create mode 100644 roles/borgmatic/.gitignore create mode 100644 roles/borgmatic/.yamllint create mode 100644 roles/borgmatic/README.md create mode 100644 roles/borgmatic/conda.dev.yml create mode 100644 roles/borgmatic/conda.prod.yml create mode 100644 roles/borgmatic/defaults/main.yml create mode 100644 roles/borgmatic/handlers/main.yml create mode 100644 roles/borgmatic/meta/main.yml create mode 100644 roles/borgmatic/molecule/default/converge.yml create mode 100644 roles/borgmatic/molecule/default/molecule.yml create mode 100644 roles/borgmatic/molecule/default/verify.yml create mode 100644 roles/borgmatic/renovate.json create mode 100644 roles/borgmatic/requirements.ci.txt create mode 100644 roles/borgmatic/requirements.txt create mode 100644 roles/borgmatic/requirements.yml create mode 100644 roles/borgmatic/tasks/config.yml create mode 100644 roles/borgmatic/tasks/handle-ssh-keys.yml create mode 100644 roles/borgmatic/tasks/install.yml create mode 100644 roles/borgmatic/tasks/integrations.yml create mode 100644 roles/borgmatic/tasks/main.yml create mode 100644 roles/borgmatic/tasks/run.yml create mode 100644 roles/borgmatic/vars/main.yml diff --git a/roles/borgmatic/.ansible-lint b/roles/borgmatic/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/borgmatic/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/borgmatic/.gitignore b/roles/borgmatic/.gitignore new file mode 100644 index 0000000..9451651 --- /dev/null +++ b/roles/borgmatic/.gitignore @@ -0,0 +1,3 @@ +.vscode +.idea +.lock diff --git a/roles/borgmatic/.yamllint b/roles/borgmatic/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/borgmatic/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/borgmatic/README.md b/roles/borgmatic/README.md new file mode 100644 index 0000000..ce5f940 --- /dev/null +++ b/roles/borgmatic/README.md @@ -0,0 +1,40 @@ +ansible-borgmatic +========= + +This is a wrapper around the `borgmatic` role from the `maxhoesel.borgbackup` collection. + +The wrapper solve the most outstading issue with the current implementation of the original role: inability to install latest (or arbitrary) versions of `borgmatic` and `borg`. + +In the case of Borg we are fetching a release from Github. + +In the case of Borgmatic we are installing it via `pipx`, as recommended by their official documentation found here: https://torsion.org/borgmatic/docs/how-to/set-up-backups/. + +Requirements +------------ + +- `maxhoesel.borgbackup` collection installed (see `requirements.yml`); + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See `molecule/default/converge.yml` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/borgmatic/conda.dev.yml b/roles/borgmatic/conda.dev.yml new file mode 100644 index 0000000..f8272ac --- /dev/null +++ b/roles/borgmatic/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-borgmatic +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/borgmatic/conda.prod.yml b/roles/borgmatic/conda.prod.yml new file mode 100644 index 0000000..2e7d5bd --- /dev/null +++ b/roles/borgmatic/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-borgmatic +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/borgmatic/defaults/main.yml b/roles/borgmatic/defaults/main.yml new file mode 100644 index 0000000..6ae71ef --- /dev/null +++ b/roles/borgmatic/defaults/main.yml @@ -0,0 +1,17 @@ +--- +borg_version: "1.4.0" +glibc_version: "2.36" +pipx_version: "1.7.1" +borg_binary_url: "https://github.com/borgbackup/borg/releases/download/{{ borg_version }}/borg-linux-glibc{{ glibc_version | replace('.', '') }}.tgz" +borgmatic_pipx_bin_dir: "/opt/borgmatic/bin" +borgmatic_schedule_oncalendar: "daily" +push_pubkey: true +ssh_key_path: "/root/borgmatic/id_ed25519" +borg_compression: "lz4" +borg_keep_hourly: 0 +borg_keep_daily: 3 +borg_keep_weekly: 3 +borg_keep_monthly: 1 +borg_keep_yearly: 0 +borg_uptime_kuma: +borg_loki: diff --git a/roles/borgmatic/handlers/main.yml b/roles/borgmatic/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/borgmatic/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/borgmatic/meta/main.yml b/roles/borgmatic/meta/main.yml new file mode 100644 index 0000000..c99f58c --- /dev/null +++ b/roles/borgmatic/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "borgmatic" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/borgmatic/molecule/default/converge.yml b/roles/borgmatic/molecule/default/converge.yml new file mode 100644 index 0000000..eabe4f7 --- /dev/null +++ b/roles/borgmatic/molecule/default/converge.yml @@ -0,0 +1,48 @@ +--- +- name: Converge + hosts: all + vars: + repo_path: "/home/borg/test-repo" + + pre_tasks: + - name: "Create a user for borg" + ansible.builtin.user: + name: borg + shell: /bin/bash + create_home: true + + - name: "Generate test data file" + ansible.builtin.copy: + dest: "/tmp/data" + content: "This is a test file!" + owner: "{{ ansible_user_id }}" + group: "{{ ansible_user_id }}" + mode: "0644" + + - name: "Ensure the repo path exists" + ansible.builtin.file: + path: "{{ repo_path }}" + state: directory + owner: "borg" + mode: "0700" + + - name: "Install openssh-server" + ansible.builtin.apt: + name: openssh-server + state: present + update_cache: true + + - name: "Start an SSH openssh-server" + ansible.builtin.systemd: + name: ssh + state: started + + roles: + - role: genlab.borgmatic + borg_source_directories: + - "/tmp/data" + borg_repo_path: "ssh://borg@localhost/./test-repo" + borg_repo_label: "test-repo" + borg_encryption_passphrase: "secret" + repo_server_inventory_hostname: ubuntu # in production this should be an Ansible inventory hostname + repo_server_user: borg diff --git a/roles/borgmatic/molecule/default/molecule.yml b/roles/borgmatic/molecule/default/molecule.yml new file mode 100644 index 0000000..2910ca3 --- /dev/null +++ b/roles/borgmatic/molecule/default/molecule.yml @@ -0,0 +1,31 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +scenario: + name: default + test_sequence: + - destroy + - create + - converge + # - idempotence + - verify diff --git a/roles/borgmatic/molecule/default/verify.yml b/roles/borgmatic/molecule/default/verify.yml new file mode 100644 index 0000000..67cdad1 --- /dev/null +++ b/roles/borgmatic/molecule/default/verify.yml @@ -0,0 +1,48 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + vars: + repo_path: "/home/borg/test-repo" + + tasks: + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Check if Borg is installed" + changed_when: false + register: borg_installed_version + ansible.builtin.command: "/usr/bin/borg --version" + + - name: "Check Borg version" + ansible.builtin.assert: + that: borg_installed_version.stdout.find(borg_version) + success_msg: "borg version {{ borg_version }} is installed and working" + fail_msg: "borg version {{ borg_version }} is not installed or not working correctly" + + - name: "Check if Borgmatic is installed" + changed_when: false + register: borgmatic_installed_version + ansible.builtin.command: + cmd: "/usr/bin/borgmatic --version" + + - name: "Check that the test repo was created" + register: test_repo_readme + failed_when: test_repo_readme.stat.exists is false + ansible.builtin.stat: + path: "{{ repo_path }}" + + - name: "Check that the systemd timer for Borgmatic is up and running" + register: timer + ansible.builtin.systemd: + name: borgmatic.timer + + - name: "Assert that the timer is running" + ansible.builtin.assert: + that: timer.status.ActiveState == "active" + success_msg: "Timer is running" + fail_msg: "Unexpected timer state: '{{ timer.status.ActiveState }}'" diff --git a/roles/borgmatic/renovate.json b/roles/borgmatic/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/borgmatic/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/borgmatic/requirements.ci.txt b/roles/borgmatic/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/borgmatic/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/borgmatic/requirements.txt b/roles/borgmatic/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/borgmatic/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/borgmatic/requirements.yml b/roles/borgmatic/requirements.yml new file mode 100644 index 0000000..e2cdc9f --- /dev/null +++ b/roles/borgmatic/requirements.yml @@ -0,0 +1,6 @@ +# requirements file +--- +collections: + - name: https://github.com/maxhoesel/ansible-collection-borgbackup + type: git + version: "v2.0.1" diff --git a/roles/borgmatic/tasks/config.yml b/roles/borgmatic/tasks/config.yml new file mode 100644 index 0000000..f030017 --- /dev/null +++ b/roles/borgmatic/tasks/config.yml @@ -0,0 +1,34 @@ +--- +- name: "Compose basic configuration for Borgmatic" + ansible.builtin.set_fact: + borgmatic_composite_config: + source_directories: "{{ borg_source_directories }}" + repositories: + - path: "{{ borg_repo_path }}" + label: "{{ borg_repo_label }}" + encryption_passphrase: "{{ borg_encryption_passphrase }}" + compression: "{{ borg_compression }}" + # CLI output configuration + list_details: true + statistics: true + exclude_caches: true + # logging verbosity: + verbosity: 1 + syslog_verbosity: 1 + monitoring_verbosity: 1 + # backup depth + keep_hourly: "{{ borg_keep_hourly }}" + keep_daily: "{{ borg_keep_daily }}" + keep_weekly: "{{ borg_keep_weekly }}" + keep_monthly: "{{ borg_keep_monthly }}" + keep_yearly: "{{ borg_keep_yearly }}" + +- name: "Add Uptime Kuma configuration" + when: borg_uptime_kuma + ansible.builtin.set_fact: + borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'uptime_kuma': borg_uptime_kuma}) }}" + +- name: "Add Loki configuration" + when: borg_loki + ansible.builtin.set_fact: + borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'loki': borg_loki}) }}" diff --git a/roles/borgmatic/tasks/handle-ssh-keys.yml b/roles/borgmatic/tasks/handle-ssh-keys.yml new file mode 100644 index 0000000..126349b --- /dev/null +++ b/roles/borgmatic/tasks/handle-ssh-keys.yml @@ -0,0 +1,25 @@ +--- +- name: "Ensure the path for SSH keys exists" + ansible.builtin.file: + path: "{{ ssh_key_path | dirname }}" + state: directory + owner: root + group: root + mode: "0700" + +- name: "Generate an ed25519 SSH key pair with 100 KDF rounds" + register: ssh_key_pair + community.crypto.openssh_keypair: + type: ed25519 + path: "{{ ssh_key_path }}" + comment: "Generated by Ansible for Borgmatic" + force: false + mode: '0600' + +- name: "Push the SSH key pair to the Borg repo host" + when: push_pubkey + delegate_to: "{{ repo_server_inventory_hostname }}" + ansible.posix.authorized_key: + user: "{{ repo_server_user }}" + key: "{{ ssh_key_pair.public_key }}" + state: present diff --git a/roles/borgmatic/tasks/install.yml b/roles/borgmatic/tasks/install.yml new file mode 100644 index 0000000..98cf198 --- /dev/null +++ b/roles/borgmatic/tasks/install.yml @@ -0,0 +1,66 @@ +--- +- name: "Ensure that system dependencies are installed" + ansible.builtin.apt: + name: + - openssh-client + - python3-pip + - python3-venv + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Install pipx" + retries: 3 + delay: 1 + ansible.builtin.pip: + name: "pipx=={{ pipx_version }}" + executable: pip3 + break_system_packages: true + +- name: "Ensure pipx binary is available in PATH" + changed_when: false + ansible.builtin.command: + cmd: pipx ensurepath + +- name: "Install borgmatic via pipx" + retries: 3 + delay: 1 + environment: + PIPX_BIN_DIR: "{{ borgmatic_pipx_bin_dir }}" + community.general.pipx: + name: borgmatic + state: present + install_deps: true + +- name: "Install Borg if the correct version is not available" + block: + # we are looking for Borg installed in a directory that Max Hoesel's role exects to find it + - name: "Get the currently installed version of Borg" + changed_when: false + register: borg_version_installed + ansible.builtin.command: + cmd: /usr/bin/borg --version + + - name: "Check that the correct version of Borg is installed" + ansible.builtin.assert: + that: borg_version_installed.stdout.find(borg_version) + fail_msg: "The expected Borg version was not found: {{ borg_version_installed }}" + success_msg: "Found the expected Borg version ({{ borg_version }})" + + rescue: + - name: "Download Borg from a custom URL: '{{ borg_binary_url }}'" + retries: 3 + delay: 1 + ansible.builtin.unarchive: + src: "{{ borg_binary_url }}" + dest: "/opt/" + remote_src: true + owner: root + group: root + mode: "0755" + + - name: "Create a symbolic link for Borg" + ansible.builtin.file: + state: link + src: "/opt/borg-dir/borg.exe" + dest: "/usr/bin/borg" diff --git a/roles/borgmatic/tasks/integrations.yml b/roles/borgmatic/tasks/integrations.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/borgmatic/tasks/integrations.yml @@ -0,0 +1 @@ +--- diff --git a/roles/borgmatic/tasks/main.yml b/roles/borgmatic/tasks/main.yml new file mode 100644 index 0000000..0207ec6 --- /dev/null +++ b/roles/borgmatic/tasks/main.yml @@ -0,0 +1,15 @@ +--- +- name: "Include tool installation tasks" + ansible.builtin.include_tasks: "install.yml" + +- name: "Include SSH key handling tasks" + ansible.builtin.include_tasks: "handle-ssh-keys.yml" + +- name: "Include configuration tasks" + ansible.builtin.include_tasks: "config.yml" + +- name: "Include tasks for third-party integrations" + ansible.builtin.include_tasks: "integrations.yml" + +- name: "Include tasks for running borgmatic" + ansible.builtin.include_tasks: "run.yml" diff --git a/roles/borgmatic/tasks/run.yml b/roles/borgmatic/tasks/run.yml new file mode 100644 index 0000000..182aa05 --- /dev/null +++ b/roles/borgmatic/tasks/run.yml @@ -0,0 +1,22 @@ +--- +# A workaround for maxhoesel.borgbackup.borgmatic that does not support custom paths +- name: "Create symbolic links for Borgmatic executables" + loop: + - borgmatic + - generate-borgmatic-config + - validate-borgmatic-config + ansible.builtin.file: + state: link + src: "{{ borgmatic_pipx_bin_dir }}/{{ item }}" + dest: /usr/bin/{{ item }} + +- name: "Configure and run Borgmatic" + ansible.builtin.include_role: + name: maxhoesel.borgbackup.borgmatic + vars: + borgmatic_install: false # we handle installation separately to get the recent version + # borgmatic_ssh_key_gen_options: "-t ed25519 -a 100" + borgmatic_ssh_key_path: "{{ ssh_key_path }}" + borgmatic_schedule_on: "{{ borgmatic_schedule_oncalendar }}" + borgmatic_schedule_max_random_delay: 600 + borgmatic_config: "{{ borgmatic_composite_config }}" diff --git a/roles/borgmatic/vars/main.yml b/roles/borgmatic/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/borgmatic/vars/main.yml @@ -0,0 +1 @@ +--- From c40b4928f71fed417f7e69494dee8a48c33c477e Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 17:22:12 +0300 Subject: [PATCH 43/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bd9d0d..e9151cd 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ ## Roles - [alertmanager](roles/alertmanager/README.md) +- [borgmatic](roles/borgmatic/README.md) - [dnsmasq](roles/dnsmasq/README.md) - [grafana](roles/grafana/README.md) - [ipmi_exporter](roles/ipmi_exporter/README.md) diff --git a/galaxy.yml b/galaxy.yml index c68bddb..409ec6a 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.9.0 +version: 0.10.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 3f4556f3b2932e9a4448d6dcd960b9c472a897ba Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Fri, 12 Dec 2025 18:35:37 +0300 Subject: [PATCH 44/96] fix some ansible-lint errors --- roles/borgmatic/defaults/main.yml | 21 +++++++------- roles/borgmatic/meta/main.yml | 4 +-- roles/borgmatic/molecule/default/converge.yml | 10 +++---- roles/borgmatic/molecule/default/verify.yml | 20 ++++++------- roles/borgmatic/tasks/config.yml | 28 +++++++++---------- roles/borgmatic/tasks/handle-ssh-keys.yml | 4 +-- roles/borgmatic/tasks/install.yml | 12 ++++---- 7 files changed, 50 insertions(+), 49 deletions(-) diff --git a/roles/borgmatic/defaults/main.yml b/roles/borgmatic/defaults/main.yml index 6ae71ef..4cfd1c1 100644 --- a/roles/borgmatic/defaults/main.yml +++ b/roles/borgmatic/defaults/main.yml @@ -1,17 +1,18 @@ --- -borg_version: "1.4.0" +borgmatic_version: "1.4.0" glibc_version: "2.36" pipx_version: "1.7.1" -borg_binary_url: "https://github.com/borgbackup/borg/releases/download/{{ borg_version }}/borg-linux-glibc{{ glibc_version | replace('.', '') }}.tgz" +borgmatic_binary_url: "https://github.com/borgbackup/borg/releases/download/{{ borgmatic_version }}/borg-linux-glibc{{ glibc_version | replace('.', '') }}.tgz" borgmatic_pipx_bin_dir: "/opt/borgmatic/bin" borgmatic_schedule_oncalendar: "daily" push_pubkey: true ssh_key_path: "/root/borgmatic/id_ed25519" -borg_compression: "lz4" -borg_keep_hourly: 0 -borg_keep_daily: 3 -borg_keep_weekly: 3 -borg_keep_monthly: 1 -borg_keep_yearly: 0 -borg_uptime_kuma: -borg_loki: +borgmatic_compression: "lz4" +borgmatic_keep_hourly: 0 +borgmatic_keep_daily: 3 +borgmatic_keep_weekly: 3 +borgmatic_keep_monthly: 1 +borgmatic_keep_yearly: 0 +borgmatic_uptime_kuma: +borgmatic_loki: +# todo: ansible-lint errors \ No newline at end of file diff --git a/roles/borgmatic/meta/main.yml b/roles/borgmatic/meta/main.yml index c99f58c..868031b 100644 --- a/roles/borgmatic/meta/main.yml +++ b/roles/borgmatic/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/borgmatic/molecule/default/converge.yml b/roles/borgmatic/molecule/default/converge.yml index eabe4f7..cb3fdb1 100644 --- a/roles/borgmatic/molecule/default/converge.yml +++ b/roles/borgmatic/molecule/default/converge.yml @@ -38,11 +38,11 @@ state: started roles: - - role: genlab.borgmatic - borg_source_directories: + - role: genlab.common.borgmatic + borgmatic_source_directories: - "/tmp/data" - borg_repo_path: "ssh://borg@localhost/./test-repo" - borg_repo_label: "test-repo" - borg_encryption_passphrase: "secret" + borgmatic_repo_path: "ssh://borg@localhost/./test-repo" + borgmatic_repo_label: "test-repo" + borgmatic_encryption_passphrase: "secret" repo_server_inventory_hostname: ubuntu # in production this should be an Ansible inventory hostname repo_server_user: borg diff --git a/roles/borgmatic/molecule/default/verify.yml b/roles/borgmatic/molecule/default/verify.yml index 67cdad1..6edcb47 100644 --- a/roles/borgmatic/molecule/default/verify.yml +++ b/roles/borgmatic/molecule/default/verify.yml @@ -11,18 +11,18 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Check if Borg is installed" changed_when: false - register: borg_installed_version + register: borgmatic_installed_version ansible.builtin.command: "/usr/bin/borg --version" - name: "Check Borg version" ansible.builtin.assert: - that: borg_installed_version.stdout.find(borg_version) - success_msg: "borg version {{ borg_version }} is installed and working" - fail_msg: "borg version {{ borg_version }} is not installed or not working correctly" + that: borgmatic_installed_version.stdout.find(borgmatic_version) + success_msg: "borg version {{ borgmatic_version }} is installed and working" + fail_msg: "borg version {{ borgmatic_version }} is not installed or not working correctly" - name: "Check if Borgmatic is installed" changed_when: false @@ -31,18 +31,18 @@ cmd: "/usr/bin/borgmatic --version" - name: "Check that the test repo was created" - register: test_repo_readme - failed_when: test_repo_readme.stat.exists is false + register: borgmatic_test_repo_readme + failed_when: borgmatic_test_repo_readme.stat.exists is false ansible.builtin.stat: path: "{{ repo_path }}" - name: "Check that the systemd timer for Borgmatic is up and running" - register: timer + register: borgmatic_timer ansible.builtin.systemd: name: borgmatic.timer - name: "Assert that the timer is running" ansible.builtin.assert: - that: timer.status.ActiveState == "active" + that: borgmatic_timer.status.ActiveState == "active" success_msg: "Timer is running" - fail_msg: "Unexpected timer state: '{{ timer.status.ActiveState }}'" + fail_msg: "Unexpected timer state: '{{ borgmatic_timer.status.ActiveState }}'" diff --git a/roles/borgmatic/tasks/config.yml b/roles/borgmatic/tasks/config.yml index f030017..78dc67d 100644 --- a/roles/borgmatic/tasks/config.yml +++ b/roles/borgmatic/tasks/config.yml @@ -2,12 +2,12 @@ - name: "Compose basic configuration for Borgmatic" ansible.builtin.set_fact: borgmatic_composite_config: - source_directories: "{{ borg_source_directories }}" + source_directories: "{{ borgmatic_source_directories }}" repositories: - - path: "{{ borg_repo_path }}" - label: "{{ borg_repo_label }}" - encryption_passphrase: "{{ borg_encryption_passphrase }}" - compression: "{{ borg_compression }}" + - path: "{{ borgmatic_repo_path }}" + label: "{{ borgmatic_repo_label }}" + encryption_passphrase: "{{ borgmatic_encryption_passphrase }}" + compression: "{{ borgmatic_compression }}" # CLI output configuration list_details: true statistics: true @@ -17,18 +17,18 @@ syslog_verbosity: 1 monitoring_verbosity: 1 # backup depth - keep_hourly: "{{ borg_keep_hourly }}" - keep_daily: "{{ borg_keep_daily }}" - keep_weekly: "{{ borg_keep_weekly }}" - keep_monthly: "{{ borg_keep_monthly }}" - keep_yearly: "{{ borg_keep_yearly }}" + keep_hourly: "{{ borgmatic_keep_hourly }}" + keep_daily: "{{ borgmatic_keep_daily }}" + keep_weekly: "{{ borgmatic_keep_weekly }}" + keep_monthly: "{{ borgmatic_keep_monthly }}" + keep_yearly: "{{ borgmatic_keep_yearly }}" - name: "Add Uptime Kuma configuration" - when: borg_uptime_kuma + when: borgmatic_uptime_kuma ansible.builtin.set_fact: - borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'uptime_kuma': borg_uptime_kuma}) }}" + borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'uptime_kuma': borgmatic_uptime_kuma}) }}" - name: "Add Loki configuration" - when: borg_loki + when: borgmatic_loki ansible.builtin.set_fact: - borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'loki': borg_loki}) }}" + borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'loki': borgmatic_loki}) }}" diff --git a/roles/borgmatic/tasks/handle-ssh-keys.yml b/roles/borgmatic/tasks/handle-ssh-keys.yml index 126349b..37db382 100644 --- a/roles/borgmatic/tasks/handle-ssh-keys.yml +++ b/roles/borgmatic/tasks/handle-ssh-keys.yml @@ -8,7 +8,7 @@ mode: "0700" - name: "Generate an ed25519 SSH key pair with 100 KDF rounds" - register: ssh_key_pair + register: borgmatic_ssh_key_pair community.crypto.openssh_keypair: type: ed25519 path: "{{ ssh_key_path }}" @@ -21,5 +21,5 @@ delegate_to: "{{ repo_server_inventory_hostname }}" ansible.posix.authorized_key: user: "{{ repo_server_user }}" - key: "{{ ssh_key_pair.public_key }}" + key: "{{ borgmatic_ssh_key_pair.public_key }}" state: present diff --git a/roles/borgmatic/tasks/install.yml b/roles/borgmatic/tasks/install.yml index 98cf198..0e6d718 100644 --- a/roles/borgmatic/tasks/install.yml +++ b/roles/borgmatic/tasks/install.yml @@ -37,22 +37,22 @@ # we are looking for Borg installed in a directory that Max Hoesel's role exects to find it - name: "Get the currently installed version of Borg" changed_when: false - register: borg_version_installed + register: borgmatic_version_installed ansible.builtin.command: cmd: /usr/bin/borg --version - name: "Check that the correct version of Borg is installed" ansible.builtin.assert: - that: borg_version_installed.stdout.find(borg_version) - fail_msg: "The expected Borg version was not found: {{ borg_version_installed }}" - success_msg: "Found the expected Borg version ({{ borg_version }})" + that: borgmatic_version_installed.stdout.find(borgmatic_version) + fail_msg: "The expected Borg version was not found: {{ borgmatic_version_installed }}" + success_msg: "Found the expected Borg version ({{ borgmatic_version }})" rescue: - - name: "Download Borg from a custom URL: '{{ borg_binary_url }}'" + - name: "Download Borg from a custom URL: '{{ borgmatic_binary_url }}'" retries: 3 delay: 1 ansible.builtin.unarchive: - src: "{{ borg_binary_url }}" + src: "{{ borgmatic_binary_url }}" dest: "/opt/" remote_src: true owner: root From 246905a2ee489fd6202276e3146a5fc35abe0660 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:20:12 +0300 Subject: [PATCH 45/96] fix ansible-lint errors --- roles/borgmatic/defaults/main.yml | 12 ++++++------ roles/borgmatic/tasks/handle-ssh-keys.yml | 6 +++--- roles/borgmatic/tasks/install.yml | 2 +- roles/borgmatic/tasks/run.yml | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/roles/borgmatic/defaults/main.yml b/roles/borgmatic/defaults/main.yml index 4cfd1c1..c96f1e8 100644 --- a/roles/borgmatic/defaults/main.yml +++ b/roles/borgmatic/defaults/main.yml @@ -1,12 +1,13 @@ --- borgmatic_version: "1.4.0" -glibc_version: "2.36" -pipx_version: "1.7.1" -borgmatic_binary_url: "https://github.com/borgbackup/borg/releases/download/{{ borgmatic_version }}/borg-linux-glibc{{ glibc_version | replace('.', '') }}.tgz" +borgmatic_glibc_version: "2.36" +borgmatic_pipx_version: "1.7.1" +borgmatic_binary_url: "\ + https://github.com/borgbackup/borg/releases/download/{{ borgmatic_version }}/borg-linux-glibc{{ borgmatic_glibc_version | replace('.', '') }}.tgz" borgmatic_pipx_bin_dir: "/opt/borgmatic/bin" borgmatic_schedule_oncalendar: "daily" -push_pubkey: true -ssh_key_path: "/root/borgmatic/id_ed25519" +borgmatic_push_pubkey: true +borgmatic_sshkey_path: "/root/borgmatic/id_ed25519" borgmatic_compression: "lz4" borgmatic_keep_hourly: 0 borgmatic_keep_daily: 3 @@ -15,4 +16,3 @@ borgmatic_keep_monthly: 1 borgmatic_keep_yearly: 0 borgmatic_uptime_kuma: borgmatic_loki: -# todo: ansible-lint errors \ No newline at end of file diff --git a/roles/borgmatic/tasks/handle-ssh-keys.yml b/roles/borgmatic/tasks/handle-ssh-keys.yml index 37db382..07e9c07 100644 --- a/roles/borgmatic/tasks/handle-ssh-keys.yml +++ b/roles/borgmatic/tasks/handle-ssh-keys.yml @@ -1,7 +1,7 @@ --- - name: "Ensure the path for SSH keys exists" ansible.builtin.file: - path: "{{ ssh_key_path | dirname }}" + path: "{{ borgmatic_sshkey_path | dirname }}" state: directory owner: root group: root @@ -11,13 +11,13 @@ register: borgmatic_ssh_key_pair community.crypto.openssh_keypair: type: ed25519 - path: "{{ ssh_key_path }}" + path: "{{ borgmatic_sshkey_path }}" comment: "Generated by Ansible for Borgmatic" force: false mode: '0600' - name: "Push the SSH key pair to the Borg repo host" - when: push_pubkey + when: borgmatic_push_pubkey delegate_to: "{{ repo_server_inventory_hostname }}" ansible.posix.authorized_key: user: "{{ repo_server_user }}" diff --git a/roles/borgmatic/tasks/install.yml b/roles/borgmatic/tasks/install.yml index 0e6d718..46f5203 100644 --- a/roles/borgmatic/tasks/install.yml +++ b/roles/borgmatic/tasks/install.yml @@ -13,7 +13,7 @@ retries: 3 delay: 1 ansible.builtin.pip: - name: "pipx=={{ pipx_version }}" + name: "pipx=={{ borgmatic_pipx_version }}" executable: pip3 break_system_packages: true diff --git a/roles/borgmatic/tasks/run.yml b/roles/borgmatic/tasks/run.yml index 182aa05..28051f5 100644 --- a/roles/borgmatic/tasks/run.yml +++ b/roles/borgmatic/tasks/run.yml @@ -16,7 +16,7 @@ vars: borgmatic_install: false # we handle installation separately to get the recent version # borgmatic_ssh_key_gen_options: "-t ed25519 -a 100" - borgmatic_ssh_key_path: "{{ ssh_key_path }}" + borgmatic_ssh_key_path: "{{ borgmatic_sshkey_path }}" borgmatic_schedule_on: "{{ borgmatic_schedule_oncalendar }}" borgmatic_schedule_max_random_delay: 600 borgmatic_config: "{{ borgmatic_composite_config }}" From b2277e43ba489305f2682f0959a2728261382ed6 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:34:19 +0300 Subject: [PATCH 46/96] remove garbage --- roles/borgmatic/.ansible-lint | 16 ---------------- roles/borgmatic/.gitignore | 3 --- roles/borgmatic/.yamllint | 8 -------- roles/borgmatic/conda.dev.yml | 11 ----------- roles/borgmatic/conda.prod.yml | 9 --------- roles/borgmatic/renovate.json | 6 ------ roles/borgmatic/requirements.ci.txt | 6 ------ roles/borgmatic/requirements.txt | 1 - roles/borgmatic/requirements.yml | 6 ------ 9 files changed, 66 deletions(-) delete mode 100644 roles/borgmatic/.ansible-lint delete mode 100644 roles/borgmatic/.gitignore delete mode 100644 roles/borgmatic/.yamllint delete mode 100644 roles/borgmatic/conda.dev.yml delete mode 100644 roles/borgmatic/conda.prod.yml delete mode 100644 roles/borgmatic/renovate.json delete mode 100644 roles/borgmatic/requirements.ci.txt delete mode 100644 roles/borgmatic/requirements.txt delete mode 100644 roles/borgmatic/requirements.yml diff --git a/roles/borgmatic/.ansible-lint b/roles/borgmatic/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/borgmatic/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/borgmatic/.gitignore b/roles/borgmatic/.gitignore deleted file mode 100644 index 9451651..0000000 --- a/roles/borgmatic/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.vscode -.idea -.lock diff --git a/roles/borgmatic/.yamllint b/roles/borgmatic/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/borgmatic/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/borgmatic/conda.dev.yml b/roles/borgmatic/conda.dev.yml deleted file mode 100644 index f8272ac..0000000 --- a/roles/borgmatic/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-borgmatic -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/borgmatic/conda.prod.yml b/roles/borgmatic/conda.prod.yml deleted file mode 100644 index 2e7d5bd..0000000 --- a/roles/borgmatic/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-borgmatic -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/borgmatic/renovate.json b/roles/borgmatic/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/borgmatic/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/borgmatic/requirements.ci.txt b/roles/borgmatic/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/borgmatic/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/borgmatic/requirements.txt b/roles/borgmatic/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/borgmatic/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 diff --git a/roles/borgmatic/requirements.yml b/roles/borgmatic/requirements.yml deleted file mode 100644 index e2cdc9f..0000000 --- a/roles/borgmatic/requirements.yml +++ /dev/null @@ -1,6 +0,0 @@ -# requirements file ---- -collections: - - name: https://github.com/maxhoesel/ansible-collection-borgbackup - type: git - version: "v2.0.1" From 57d9c0d3cbce922d05f26c4ce0b9190be4080a71 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:37:18 +0300 Subject: [PATCH 47/96] update dependencies --- .github/workflows/molecule.yml | 8 -------- requirements.yml | 3 +++ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml index 5fcb13d..2fde508 100644 --- a/.github/workflows/molecule.yml +++ b/.github/workflows/molecule.yml @@ -78,14 +78,6 @@ jobs: run: ansible-galaxy install -r requirements.yml shell: micromamba-shell {0} - - name: "Install community.general collection" - run: ansible-galaxy collection install community.general - shell: micromamba-shell {0} - - - name: "Install community.grafana collection" - run: ansible-galaxy collection install community.grafana - shell: micromamba-shell {0} - - name: "Run Molecule tests" if: ${{ matrix.role != '__no_role__' }} working-directory: ${{ matrix.role }} diff --git a/requirements.yml b/requirements.yml index a0cd255..493187f 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,3 +1,6 @@ --- collections: - name: ansible.posix + - name: community.general + - name: community.grafana + - name: community.docker From 8bf9916739fef07b52131a46e0b30445a4934b13 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:39:41 +0300 Subject: [PATCH 48/96] update dependencies --- requirements.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.yml b/requirements.yml index 493187f..b5c13cc 100644 --- a/requirements.yml +++ b/requirements.yml @@ -4,3 +4,4 @@ collections: - name: community.general - name: community.grafana - name: community.docker + - name: community.crypto From b142cb1bcaa89f2695b9c0b40a6875226219ea9a Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:43:19 +0300 Subject: [PATCH 49/96] update dependencies --- requirements.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.yml b/requirements.yml index b5c13cc..9fbcca8 100644 --- a/requirements.yml +++ b/requirements.yml @@ -5,3 +5,5 @@ collections: - name: community.grafana - name: community.docker - name: community.crypto +roles: + - name: maxhoesel.borgbackup.borgmatic From 2b1f2497149b8ac50967bfff05a19706f9a6b81d Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:49:15 +0300 Subject: [PATCH 50/96] update dependencies --- requirements.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.yml b/requirements.yml index 9fbcca8..e558ead 100644 --- a/requirements.yml +++ b/requirements.yml @@ -6,4 +6,4 @@ collections: - name: community.docker - name: community.crypto roles: - - name: maxhoesel.borgbackup.borgmatic + - name: maxhoesel.borgmatic From 85333e5455a0379668274578dee8a7fd84d8a4f3 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Mon, 15 Dec 2025 12:51:17 +0300 Subject: [PATCH 51/96] update dependencies --- requirements.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.yml b/requirements.yml index e558ead..093988a 100644 --- a/requirements.yml +++ b/requirements.yml @@ -5,5 +5,4 @@ collections: - name: community.grafana - name: community.docker - name: community.crypto -roles: - - name: maxhoesel.borgmatic + - name: maxhoesel.borgbackup From d8d3c2df671bd43cfc6be275930dec806549cc19 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 11:40:16 +0300 Subject: [PATCH 52/96] add smartctl_exporter role --- roles/smartctl_exporter/.ansible-lint | 16 ++++++ roles/smartctl_exporter/.gitignore | 2 + roles/smartctl_exporter/.yamllint | 8 +++ roles/smartctl_exporter/README.md | 56 +++++++++++++++++++ roles/smartctl_exporter/conda.dev.yml | 11 ++++ roles/smartctl_exporter/conda.prod.yml | 9 +++ roles/smartctl_exporter/defaults/main.yml | 14 +++++ roles/smartctl_exporter/handlers/main.yml | 7 +++ roles/smartctl_exporter/meta/main.yml | 17 ++++++ .../molecule/default/converge.yml | 7 +++ .../molecule/default/molecule.yml | 27 +++++++++ .../molecule/default/verify.yml | 37 ++++++++++++ roles/smartctl_exporter/renovate.json | 6 ++ roles/smartctl_exporter/requirements.ci.txt | 6 ++ roles/smartctl_exporter/requirements.txt | 1 + roles/smartctl_exporter/requirements.yml | 3 + roles/smartctl_exporter/tasks/configure.yml | 10 ++++ roles/smartctl_exporter/tasks/install.yml | 51 +++++++++++++++++ roles/smartctl_exporter/tasks/main.yml | 6 ++ .../templates/smartctl_exporter.service.j2 | 46 +++++++++++++++ roles/smartctl_exporter/vars/main.yml | 1 + 21 files changed, 341 insertions(+) create mode 100644 roles/smartctl_exporter/.ansible-lint create mode 100644 roles/smartctl_exporter/.gitignore create mode 100644 roles/smartctl_exporter/.yamllint create mode 100644 roles/smartctl_exporter/README.md create mode 100644 roles/smartctl_exporter/conda.dev.yml create mode 100644 roles/smartctl_exporter/conda.prod.yml create mode 100644 roles/smartctl_exporter/defaults/main.yml create mode 100644 roles/smartctl_exporter/handlers/main.yml create mode 100644 roles/smartctl_exporter/meta/main.yml create mode 100644 roles/smartctl_exporter/molecule/default/converge.yml create mode 100644 roles/smartctl_exporter/molecule/default/molecule.yml create mode 100644 roles/smartctl_exporter/molecule/default/verify.yml create mode 100644 roles/smartctl_exporter/renovate.json create mode 100644 roles/smartctl_exporter/requirements.ci.txt create mode 100644 roles/smartctl_exporter/requirements.txt create mode 100644 roles/smartctl_exporter/requirements.yml create mode 100644 roles/smartctl_exporter/tasks/configure.yml create mode 100644 roles/smartctl_exporter/tasks/install.yml create mode 100644 roles/smartctl_exporter/tasks/main.yml create mode 100644 roles/smartctl_exporter/templates/smartctl_exporter.service.j2 create mode 100644 roles/smartctl_exporter/vars/main.yml diff --git a/roles/smartctl_exporter/.ansible-lint b/roles/smartctl_exporter/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/smartctl_exporter/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/smartctl_exporter/.gitignore b/roles/smartctl_exporter/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/smartctl_exporter/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/smartctl_exporter/.yamllint b/roles/smartctl_exporter/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/smartctl_exporter/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/smartctl_exporter/README.md b/roles/smartctl_exporter/README.md new file mode 100644 index 0000000..1c1de90 --- /dev/null +++ b/roles/smartctl_exporter/README.md @@ -0,0 +1,56 @@ +genlab.smartctl-exporter +========= + +```smartctl``` is a command-line utility used to control and monitor the Self-Monitoring, Analysis and Reporting Technology (SMART) system in hard disk drives (HDDs) and solid-state drives (SSDs). ```smartctl-exporter``` exports ```smartctl``` statistics to Prometheus. + +Requirements +------------ + +smartctl + +Role Variables +-------------- +The exporter will scan the system for available devices if no ```--smartctl.device``` flags are used. The format of web.config file id described [here](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md). If ```smartctl_exp_source_web_config_dir``` is defined, role searches for ```web_smartctl.conf``` file and copy it to target host to ```smartctl_exp_web_config_dir``` directory +```yaml +--- +smartctl_exp_port: "localhost:9633" # Addresses on which to expose metrics and web interface. Repeatable for multiple addresses. +smartctl_exp_version: "0.14.0" # exporter version to install +smartctl_exp_dir: "/etc/exporters" # where to download and unarchive expoter +smartctl_exp_web_config_dir: "/etc/exporters/config" # Path to configuration file that can enable TLS or authentication +smartctl_exp_interval: "60s" # The interval between smartctl polls +smartctl_exp_rescan: "10m" # The interval between rescanning for new/disappeared devices. If the interval is smaller than 1s no rescanning takes place. If any devices are configured with smartctl.device also no rescanning takes place. +smartctl_exp_devices: [] # The device to monitor (repeatable) +smartctl_exp_device_exclude: "" #Regexp of devices to exclude from automatic scanning. (mutually exclusive to device-include) +smartctl_exp_device_include: "" # Regexp of devices to include in automatic scanning. (mutually exclusive to device-exclude) +smartctl_exp_web_telemetry_path: "/metrics" # Path under which to expose metrics +smartctl_exp_log_level: "info" # Only log messages with the given severity or above. One of: [debug, info, warn, error] +smartctl_exp_log_format: "logfmt" # Output format of log messages. One of: [logfmt, json] +smartctl_exp_args: # --version --web.systemd-socket + +smartctl_exp_source_web_config_dir: "" +``` +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: +- role: genlab.smartctl_exporter + smartctl_exp_version: "0.14.0" + smartctl_exp_dir: "/etc/exporters" + smartctl_exp_config_dir: "/etc/exporters/config" +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/smartctl_exporter/conda.dev.yml b/roles/smartctl_exporter/conda.dev.yml new file mode 100644 index 0000000..fb974b7 --- /dev/null +++ b/roles/smartctl_exporter/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/smartctl_exporter/conda.prod.yml b/roles/smartctl_exporter/conda.prod.yml new file mode 100644 index 0000000..d6d12d6 --- /dev/null +++ b/roles/smartctl_exporter/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/smartctl_exporter/defaults/main.yml b/roles/smartctl_exporter/defaults/main.yml new file mode 100644 index 0000000..20877d0 --- /dev/null +++ b/roles/smartctl_exporter/defaults/main.yml @@ -0,0 +1,14 @@ +--- +smartctl_exp_web_listen_address: "localhost:9633" +smartctl_exp_args: "" +smartctl_exp_version: "0.14.0" +smartctl_exp_dir: "/etc/exporters" +smartctl_exp_web_config_dir: "/etc/exporters/config" +smartctl_exp_interval: "60s" +smartctl_exp_rescan: "10m" +smartctl_exp_devices: [] +smartctl_exp_device_exclude: "" +smartctl_exp_device_include: "" +smartctl_exp_web_telemetry_path: "/metrics" +smartctl_exp_log_level: "info" +smartctl_exp_log_format: "logfmt" diff --git a/roles/smartctl_exporter/handlers/main.yml b/roles/smartctl_exporter/handlers/main.yml new file mode 100644 index 0000000..f9173f1 --- /dev/null +++ b/roles/smartctl_exporter/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "(Re)start and enable smartctl_exporter" + ansible.builtin.systemd_service: + name: smartctl_exporter.service + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/smartctl_exporter/meta/main.yml b/roles/smartctl_exporter/meta/main.yml new file mode 100644 index 0000000..a2b950c --- /dev/null +++ b/roles/smartctl_exporter/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "smartctl_exporter" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/smartctl_exporter/molecule/default/converge.yml b/roles/smartctl_exporter/molecule/default/converge.yml new file mode 100644 index 0000000..47fd57a --- /dev/null +++ b/roles/smartctl_exporter/molecule/default/converge.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.smartctl_exporter + smartctl_exp_dir: "/etc/exporters" + smartctl_exp_config_dir: "/etc/exporters/config" diff --git a/roles/smartctl_exporter/molecule/default/molecule.yml b/roles/smartctl_exporter/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/smartctl_exporter/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/smartctl_exporter/molecule/default/verify.yml b/roles/smartctl_exporter/molecule/default/verify.yml new file mode 100644 index 0000000..683e7b0 --- /dev/null +++ b/roles/smartctl_exporter/molecule/default/verify.yml @@ -0,0 +1,37 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Check if smartctl_exporter is installed" + changed_when: false + ansible.builtin.command: "smartctl_exporter --version" + register: smartctl_exporter_installed_version + + - name: "Check smartctl_exporter version" + ansible.builtin.assert: + that: "smartctl_exporter_installed_version.stderr is regex('{{ smartctl_exp_version }}')" + success_msg: "smartctl_exporter version {{ smartctl_exp_version }} is installed and working" + fail_msg: "smartctl_exporter version {{ smartctl_exp_version }} is not installed or not working correctly" + + # kics-scan ignore-block + - name: "Check if /metrics endpoint is reachable" + ansible.builtin.uri: + url: "http://{{ smartctl_exp_web_listen_address }}/metrics" + return_content: true + status_code: 200 + timeout: 5 + register: metrics_check + + - name: "Fail if /metrics doesn't contain smartctl_exporter_build_info line" + ansible.builtin.assert: + that: "'smartctl_exporter_build_info' in metrics_check.content" + fail_msg: "smartctl_exporter /metrics endpoint doesn't contain smartctl_exporter_build_info line!" + success_msg: "smartctl_exporter /metrics endpoint contains smartctl_exporter_build_info line!" diff --git a/roles/smartctl_exporter/renovate.json b/roles/smartctl_exporter/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/smartctl_exporter/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/smartctl_exporter/requirements.ci.txt b/roles/smartctl_exporter/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/smartctl_exporter/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/smartctl_exporter/requirements.txt b/roles/smartctl_exporter/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/smartctl_exporter/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/smartctl_exporter/requirements.yml b/roles/smartctl_exporter/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/smartctl_exporter/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/smartctl_exporter/tasks/configure.yml b/roles/smartctl_exporter/tasks/configure.yml new file mode 100644 index 0000000..d939027 --- /dev/null +++ b/roles/smartctl_exporter/tasks/configure.yml @@ -0,0 +1,10 @@ +--- +- name: "Upload smartctl_exporter local configure file" + notify: "(Re)start and enable smartctl_exporter" + when: smartctl_exp_source_web_config_dir is defined and smartctl_exp_source_web_config_dir | length > 0 + ansible.builtin.template: + src: "{{ smartctl_exp_source_web_config_dir }}/web_smartctl.conf" + dest: "{{ smartctl_exp_web_config_dir }}/web_smartctl.conf" + owner: root + group: root + mode: '0640' diff --git a/roles/smartctl_exporter/tasks/install.yml b/roles/smartctl_exporter/tasks/install.yml new file mode 100644 index 0000000..7e7d85f --- /dev/null +++ b/roles/smartctl_exporter/tasks/install.yml @@ -0,0 +1,51 @@ +--- +- name: "Install smartctl_exporter from binary" + block: + - name: "Check smartctl_exporter version" + changed_when: false + ansible.builtin.command: + cmd: "smartctl_exporter --version" + register: smartctl_exporter_ver + + - name: "Assert version correctness" + ansible.builtin.assert: + that: "smartctl_exporter_ver.stderr is regex('{{ smartctl_exp_version }}')" + success_msg: "smartctl_exporter version {{ smartctl_exp_version }} is installed and working" + fail_msg: "smartctl_exporter version {{ smartctl_exp_version }} is not installed or not working correctly" + + rescue: + - name: "Create smartctl_exporter directories" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0755" + with_items: + - "{{ smartctl_exp_dir }}" + - "{{ smartctl_exp_config_dir }}" + + - name: "Unarchive smartctl_exporter tar file" + notify: "(Re)start and enable smartctl_exporter" + ansible.builtin.unarchive: + src: "https://github.com/prometheus-community/smartctl_exporter/releases/\ + download/v{{ smartctl_exp_version }}/smartctl_exporter-{{ smartctl_exp_version }}.linux-amd64.tar.gz" + dest: "{{ smartctl_exp_dir }}" + remote_src: true + + - name: "Move smartctl_exporter binary" + ansible.builtin.copy: + src: "{{ smartctl_exp_dir }}/smartctl_exporter-{{ smartctl_exp_version }}.linux-amd64/smartctl_exporter" + dest: "/usr/local/bin/smartctl_exporter" + mode: "0755" + owner: root + group: root + remote_src: true + +- name: Create systemd service file + ansible.builtin.template: + src: smartctl_exporter.service.j2 + dest: /etc/systemd/system/smartctl_exporter.service + owner: root + group: root + mode: '0644' diff --git a/roles/smartctl_exporter/tasks/main.yml b/roles/smartctl_exporter/tasks/main.yml new file mode 100644 index 0000000..76dd507 --- /dev/null +++ b/roles/smartctl_exporter/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: "Include installation tasks" + ansible.builtin.include_tasks: "install.yml" + +- name: "Including configuration tasks" + ansible.builtin.include_tasks: "configure.yml" diff --git a/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 b/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 new file mode 100644 index 0000000..a9bfce5 --- /dev/null +++ b/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 @@ -0,0 +1,46 @@ +[Unit] +Description=Smartctl Exporter +Documentation=https://github.com/prometheus-community/smartctl_exporter +After=network.target +StartLimitIntervalSec=120 +StartLimitBurst=5 + +[Service] +Type=simple +User=root +Group=root +ExecStart=/usr/local/bin/smartctl_exporter \ + --web.listen-address={{ smartctl_exp_web_listen_address }} \ + --smartctl.interval={{ smartctl_exp_interval }} \ + --smartctl.rescan={{ smartctl_exp_rescan }} \ +{% if smartctl_exp_devices is defined and smartctl_exp_devices | length > 0 %} + {% for device in smartctl_exp_devices %} + --smartctl.device={{ device }} \ + {% endfor %} +{% endif %} + --smartctl.device-exclude={{ smartctl_exp_device_exclude }} \ + --smartctl.device-include={{ smartctl_exp_device_include }} \ + --web.telemetry-path={{ smartctl_exp_web_telemetry_path }} \ + --log.level={{ smartctl_exp_log_level }} \ + --log.format={{ smartctl_exp_log_format }} \ +{% if smartctl_exp_args is defined and smartctl_exp_args | length > 0 %} + {{ smartctl_exp_args }} \ +{% endif %} +{% if smartctl_exp_source_web_config_dir is defined and smartctl_exp_source_web_config_dir | length > 0 %} \ + --web.config.file={{ smartctl_exp_web_config_dir }}/web_smartctl.conf +{% endif %} + +SyslogIdentifier=smartctl_exporter +Restart=on-failure +RestartSec=5 + +ProtectHome=yes +NoNewPrivileges=yes +ProtectSystem=strict +ProtectControlGroups=true +ProtectKernelModules=true +ProtectKernelTunables=yes +ProtectSystem=full + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/smartctl_exporter/vars/main.yml b/roles/smartctl_exporter/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/smartctl_exporter/vars/main.yml @@ -0,0 +1 @@ +--- From 48652534ceba4e8c49b8fc4be21cc99567785812 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 11:41:34 +0300 Subject: [PATCH 53/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e9151cd..181aa68 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - [ipmi_exporter](roles/ipmi_exporter/README.md) - [mount_device](roles/mount_device/README.md) - [prometheus](roles/prometheus/README.md) +- [smartctl_exporter](roles/smartctl_exporter/README.md) - [ufw](roles/ufw/README.md) - [wg_hub](roles/wg_hub/README.md) - [wg_spoke](roles/wg_spoke/README.md) \ No newline at end of file diff --git a/galaxy.yml b/galaxy.yml index 409ec6a..4bb7cfd 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.10.0 +version: 0.11.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From f4b945d1edc2f104af4fbde1da2696b509815397 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 11:56:52 +0300 Subject: [PATCH 54/96] fix ansible-lint errors --- roles/smartctl_exporter/defaults/main.yml | 26 +++++++++---------- roles/smartctl_exporter/meta/main.yml | 4 +-- .../molecule/default/converge.yml | 4 +-- .../molecule/default/verify.yml | 14 +++++----- roles/smartctl_exporter/tasks/configure.yml | 2 +- roles/smartctl_exporter/tasks/install.yml | 14 +++++----- .../templates/smartctl_exporter.service.j2 | 26 +++++++++---------- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/roles/smartctl_exporter/defaults/main.yml b/roles/smartctl_exporter/defaults/main.yml index 20877d0..8c146b9 100644 --- a/roles/smartctl_exporter/defaults/main.yml +++ b/roles/smartctl_exporter/defaults/main.yml @@ -1,14 +1,14 @@ --- -smartctl_exp_web_listen_address: "localhost:9633" -smartctl_exp_args: "" -smartctl_exp_version: "0.14.0" -smartctl_exp_dir: "/etc/exporters" -smartctl_exp_web_config_dir: "/etc/exporters/config" -smartctl_exp_interval: "60s" -smartctl_exp_rescan: "10m" -smartctl_exp_devices: [] -smartctl_exp_device_exclude: "" -smartctl_exp_device_include: "" -smartctl_exp_web_telemetry_path: "/metrics" -smartctl_exp_log_level: "info" -smartctl_exp_log_format: "logfmt" +smartctl_exporter_web_listen_address: "localhost:9633" +smartctl_exporter_args: "" +smartctl_exporter_version: "0.14.0" +smartctl_exporter_dir: "/etc/exporters" +smartctl_exporter_web_config_dir: "/etc/exporters/config" +smartctl_exporter_interval: "60s" +smartctl_exporter_rescan: "10m" +smartctl_exporter_devices: [] +smartctl_exporter_device_exclude: "" +smartctl_exporter_device_include: "" +smartctl_exporter_web_telemetry_path: "/metrics" +smartctl_exporter_log_level: "info" +smartctl_exporter_log_format: "logfmt" diff --git a/roles/smartctl_exporter/meta/main.yml b/roles/smartctl_exporter/meta/main.yml index a2b950c..cde1c90 100644 --- a/roles/smartctl_exporter/meta/main.yml +++ b/roles/smartctl_exporter/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/smartctl_exporter/molecule/default/converge.yml b/roles/smartctl_exporter/molecule/default/converge.yml index 47fd57a..fcbdf59 100644 --- a/roles/smartctl_exporter/molecule/default/converge.yml +++ b/roles/smartctl_exporter/molecule/default/converge.yml @@ -2,6 +2,6 @@ - name: Converge hosts: all roles: - - role: genlab.smartctl_exporter - smartctl_exp_dir: "/etc/exporters" + - role: genlab.common.smartctl_exporter + smartctl_exporter_dir: "/etc/exporters" smartctl_exp_config_dir: "/etc/exporters/config" diff --git a/roles/smartctl_exporter/molecule/default/verify.yml b/roles/smartctl_exporter/molecule/default/verify.yml index 683e7b0..de66787 100644 --- a/roles/smartctl_exporter/molecule/default/verify.yml +++ b/roles/smartctl_exporter/molecule/default/verify.yml @@ -8,7 +8,7 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Check if smartctl_exporter is installed" changed_when: false @@ -17,21 +17,21 @@ - name: "Check smartctl_exporter version" ansible.builtin.assert: - that: "smartctl_exporter_installed_version.stderr is regex('{{ smartctl_exp_version }}')" - success_msg: "smartctl_exporter version {{ smartctl_exp_version }} is installed and working" - fail_msg: "smartctl_exporter version {{ smartctl_exp_version }} is not installed or not working correctly" + that: "smartctl_exporter_installed_version.stderr is regex('{{ smartctl_exporter_version }}')" + success_msg: "smartctl_exporter version {{ smartctl_exporter_version }} is installed and working" + fail_msg: "smartctl_exporter version {{ smartctl_exporter_version }} is not installed or not working correctly" # kics-scan ignore-block - name: "Check if /metrics endpoint is reachable" ansible.builtin.uri: - url: "http://{{ smartctl_exp_web_listen_address }}/metrics" + url: "http://{{ smartctl_exporter_web_listen_address }}/metrics" return_content: true status_code: 200 timeout: 5 - register: metrics_check + register: smartctl_exporter_metrics_check - name: "Fail if /metrics doesn't contain smartctl_exporter_build_info line" ansible.builtin.assert: - that: "'smartctl_exporter_build_info' in metrics_check.content" + that: "'smartctl_exporter_build_info' in smartctl_exporter_metrics_check.content" fail_msg: "smartctl_exporter /metrics endpoint doesn't contain smartctl_exporter_build_info line!" success_msg: "smartctl_exporter /metrics endpoint contains smartctl_exporter_build_info line!" diff --git a/roles/smartctl_exporter/tasks/configure.yml b/roles/smartctl_exporter/tasks/configure.yml index d939027..61ef10c 100644 --- a/roles/smartctl_exporter/tasks/configure.yml +++ b/roles/smartctl_exporter/tasks/configure.yml @@ -4,7 +4,7 @@ when: smartctl_exp_source_web_config_dir is defined and smartctl_exp_source_web_config_dir | length > 0 ansible.builtin.template: src: "{{ smartctl_exp_source_web_config_dir }}/web_smartctl.conf" - dest: "{{ smartctl_exp_web_config_dir }}/web_smartctl.conf" + dest: "{{ smartctl_exporter_web_config_dir }}/web_smartctl.conf" owner: root group: root mode: '0640' diff --git a/roles/smartctl_exporter/tasks/install.yml b/roles/smartctl_exporter/tasks/install.yml index 7e7d85f..3ddaafc 100644 --- a/roles/smartctl_exporter/tasks/install.yml +++ b/roles/smartctl_exporter/tasks/install.yml @@ -9,9 +9,9 @@ - name: "Assert version correctness" ansible.builtin.assert: - that: "smartctl_exporter_ver.stderr is regex('{{ smartctl_exp_version }}')" - success_msg: "smartctl_exporter version {{ smartctl_exp_version }} is installed and working" - fail_msg: "smartctl_exporter version {{ smartctl_exp_version }} is not installed or not working correctly" + that: "smartctl_exporter_ver.stderr is regex('{{ smartctl_exporter_version }}')" + success_msg: "smartctl_exporter version {{ smartctl_exporter_version }} is installed and working" + fail_msg: "smartctl_exporter version {{ smartctl_exporter_version }} is not installed or not working correctly" rescue: - name: "Create smartctl_exporter directories" @@ -22,20 +22,20 @@ group: root mode: "0755" with_items: - - "{{ smartctl_exp_dir }}" + - "{{ smartctl_exporter_dir }}" - "{{ smartctl_exp_config_dir }}" - name: "Unarchive smartctl_exporter tar file" notify: "(Re)start and enable smartctl_exporter" ansible.builtin.unarchive: src: "https://github.com/prometheus-community/smartctl_exporter/releases/\ - download/v{{ smartctl_exp_version }}/smartctl_exporter-{{ smartctl_exp_version }}.linux-amd64.tar.gz" - dest: "{{ smartctl_exp_dir }}" + download/v{{ smartctl_exporter_version }}/smartctl_exporter-{{ smartctl_exporter_version }}.linux-amd64.tar.gz" + dest: "{{ smartctl_exporter_dir }}" remote_src: true - name: "Move smartctl_exporter binary" ansible.builtin.copy: - src: "{{ smartctl_exp_dir }}/smartctl_exporter-{{ smartctl_exp_version }}.linux-amd64/smartctl_exporter" + src: "{{ smartctl_exporter_dir }}/smartctl_exporter-{{ smartctl_exporter_version }}.linux-amd64/smartctl_exporter" dest: "/usr/local/bin/smartctl_exporter" mode: "0755" owner: root diff --git a/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 b/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 index a9bfce5..abe2a5b 100644 --- a/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 +++ b/roles/smartctl_exporter/templates/smartctl_exporter.service.j2 @@ -10,24 +10,24 @@ Type=simple User=root Group=root ExecStart=/usr/local/bin/smartctl_exporter \ - --web.listen-address={{ smartctl_exp_web_listen_address }} \ - --smartctl.interval={{ smartctl_exp_interval }} \ - --smartctl.rescan={{ smartctl_exp_rescan }} \ -{% if smartctl_exp_devices is defined and smartctl_exp_devices | length > 0 %} - {% for device in smartctl_exp_devices %} + --web.listen-address={{ smartctl_exporter_web_listen_address }} \ + --smartctl.interval={{ smartctl_exporter_interval }} \ + --smartctl.rescan={{ smartctl_exporter_rescan }} \ +{% if smartctl_exporter_devices is defined and smartctl_exporter_devices | length > 0 %} + {% for device in smartctl_exporter_devices %} --smartctl.device={{ device }} \ {% endfor %} {% endif %} - --smartctl.device-exclude={{ smartctl_exp_device_exclude }} \ - --smartctl.device-include={{ smartctl_exp_device_include }} \ - --web.telemetry-path={{ smartctl_exp_web_telemetry_path }} \ - --log.level={{ smartctl_exp_log_level }} \ - --log.format={{ smartctl_exp_log_format }} \ -{% if smartctl_exp_args is defined and smartctl_exp_args | length > 0 %} - {{ smartctl_exp_args }} \ + --smartctl.device-exclude={{ smartctl_exporter_device_exclude }} \ + --smartctl.device-include={{ smartctl_exporter_device_include }} \ + --web.telemetry-path={{ smartctl_exporter_web_telemetry_path }} \ + --log.level={{ smartctl_exporter_log_level }} \ + --log.format={{ smartctl_exporter_log_format }} \ +{% if smartctl_exporter_args is defined and smartctl_exporter_args | length > 0 %} + {{ smartctl_exporter_args }} \ {% endif %} {% if smartctl_exp_source_web_config_dir is defined and smartctl_exp_source_web_config_dir | length > 0 %} \ - --web.config.file={{ smartctl_exp_web_config_dir }}/web_smartctl.conf + --web.config.file={{ smartctl_exporter_web_config_dir }}/web_smartctl.conf {% endif %} SyslogIdentifier=smartctl_exporter From 33ec8daca8b663c3c1daf66c45b9c688fde5ee20 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 11:57:32 +0300 Subject: [PATCH 55/96] remove garbage --- roles/smartctl_exporter/.ansible-lint | 16 ---------------- roles/smartctl_exporter/.yamllint | 8 -------- roles/smartctl_exporter/conda.dev.yml | 11 ----------- roles/smartctl_exporter/conda.prod.yml | 9 --------- roles/smartctl_exporter/renovate.json | 6 ------ roles/smartctl_exporter/requirements.ci.txt | 6 ------ roles/smartctl_exporter/requirements.txt | 1 - roles/smartctl_exporter/requirements.yml | 3 --- 8 files changed, 60 deletions(-) delete mode 100644 roles/smartctl_exporter/.ansible-lint delete mode 100644 roles/smartctl_exporter/.yamllint delete mode 100644 roles/smartctl_exporter/conda.dev.yml delete mode 100644 roles/smartctl_exporter/conda.prod.yml delete mode 100644 roles/smartctl_exporter/renovate.json delete mode 100644 roles/smartctl_exporter/requirements.ci.txt delete mode 100644 roles/smartctl_exporter/requirements.txt delete mode 100644 roles/smartctl_exporter/requirements.yml diff --git a/roles/smartctl_exporter/.ansible-lint b/roles/smartctl_exporter/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/smartctl_exporter/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/smartctl_exporter/.yamllint b/roles/smartctl_exporter/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/smartctl_exporter/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/smartctl_exporter/conda.dev.yml b/roles/smartctl_exporter/conda.dev.yml deleted file mode 100644 index fb974b7..0000000 --- a/roles/smartctl_exporter/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/smartctl_exporter/conda.prod.yml b/roles/smartctl_exporter/conda.prod.yml deleted file mode 100644 index d6d12d6..0000000 --- a/roles/smartctl_exporter/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/smartctl_exporter/renovate.json b/roles/smartctl_exporter/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/smartctl_exporter/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/smartctl_exporter/requirements.ci.txt b/roles/smartctl_exporter/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/smartctl_exporter/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/smartctl_exporter/requirements.txt b/roles/smartctl_exporter/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/smartctl_exporter/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 diff --git a/roles/smartctl_exporter/requirements.yml b/roles/smartctl_exporter/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/smartctl_exporter/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From 400834cc3b039c230ad5ec0d889593d4b6990830 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 12:24:14 +0300 Subject: [PATCH 56/96] add curl_scheduled role --- roles/curl_scheduled/.ansible-lint | 16 ++++ roles/curl_scheduled/.gitignore | 2 + roles/curl_scheduled/.yamllint | 8 ++ roles/curl_scheduled/README.md | 41 ++++++++++ roles/curl_scheduled/conda.dev.yml | 11 +++ roles/curl_scheduled/conda.prod.yml | 9 +++ roles/curl_scheduled/defaults/main.yml | 4 + roles/curl_scheduled/handlers/main.yml | 1 + roles/curl_scheduled/meta/main.yml | 17 ++++ .../molecule/default/converge.yml | 45 +++++++++++ .../molecule/default/molecule.yml | 27 +++++++ .../molecule/default/verify.yml | 81 +++++++++++++++++++ roles/curl_scheduled/renovate.json | 6 ++ roles/curl_scheduled/requirements.ci.txt | 6 ++ roles/curl_scheduled/requirements.txt | 1 + roles/curl_scheduled/requirements.yml | 3 + roles/curl_scheduled/tasks/deploy-service.yml | 33 ++++++++ roles/curl_scheduled/tasks/main.yml | 19 +++++ .../templates/placeholder.service | 21 +++++ .../templates/placeholder.timer | 12 +++ roles/curl_scheduled/vars/main.yml | 2 + 21 files changed, 365 insertions(+) create mode 100644 roles/curl_scheduled/.ansible-lint create mode 100644 roles/curl_scheduled/.gitignore create mode 100644 roles/curl_scheduled/.yamllint create mode 100644 roles/curl_scheduled/README.md create mode 100644 roles/curl_scheduled/conda.dev.yml create mode 100644 roles/curl_scheduled/conda.prod.yml create mode 100644 roles/curl_scheduled/defaults/main.yml create mode 100644 roles/curl_scheduled/handlers/main.yml create mode 100644 roles/curl_scheduled/meta/main.yml create mode 100644 roles/curl_scheduled/molecule/default/converge.yml create mode 100644 roles/curl_scheduled/molecule/default/molecule.yml create mode 100644 roles/curl_scheduled/molecule/default/verify.yml create mode 100644 roles/curl_scheduled/renovate.json create mode 100644 roles/curl_scheduled/requirements.ci.txt create mode 100644 roles/curl_scheduled/requirements.txt create mode 100644 roles/curl_scheduled/requirements.yml create mode 100644 roles/curl_scheduled/tasks/deploy-service.yml create mode 100644 roles/curl_scheduled/tasks/main.yml create mode 100644 roles/curl_scheduled/templates/placeholder.service create mode 100644 roles/curl_scheduled/templates/placeholder.timer create mode 100644 roles/curl_scheduled/vars/main.yml diff --git a/roles/curl_scheduled/.ansible-lint b/roles/curl_scheduled/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/curl_scheduled/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/curl_scheduled/.gitignore b/roles/curl_scheduled/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/curl_scheduled/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/curl_scheduled/.yamllint b/roles/curl_scheduled/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/curl_scheduled/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/curl_scheduled/README.md b/roles/curl_scheduled/README.md new file mode 100644 index 0000000..285bbb0 --- /dev/null +++ b/roles/curl_scheduled/README.md @@ -0,0 +1,41 @@ +curl-scheduled +========= + +Configure curl to run on schedule by deploying a systemd service + timer. Useful for sending heartbeats. + +Requirements +------------ + +None + +Role Variables +-------------- + +- `args`: arguments to the curl command + +- `url`: address to be accessed by curl + +- `schedule`: string compatible with systemd timer `OnSchedule` option (default: `minutely`) + + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See `molecule/default/converge.yml`. + +License +------- + +BSD + +Author Information +------------------ + +msayganova@genlab.llc + +corvus-migratorius@proton.me diff --git a/roles/curl_scheduled/conda.dev.yml b/roles/curl_scheduled/conda.dev.yml new file mode 100644 index 0000000..c887590 --- /dev/null +++ b/roles/curl_scheduled/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-curl-scheduled +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/curl_scheduled/conda.prod.yml b/roles/curl_scheduled/conda.prod.yml new file mode 100644 index 0000000..ab2ae6a --- /dev/null +++ b/roles/curl_scheduled/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-curl-scheduled +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/curl_scheduled/defaults/main.yml b/roles/curl_scheduled/defaults/main.yml new file mode 100644 index 0000000..ae69ac0 --- /dev/null +++ b/roles/curl_scheduled/defaults/main.yml @@ -0,0 +1,4 @@ +--- +default_curl_cmd: "/usr/bin/curl" +default_curl_args: "-fsS -m 10" +default_schedule: "minutely" diff --git a/roles/curl_scheduled/handlers/main.yml b/roles/curl_scheduled/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/curl_scheduled/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/curl_scheduled/meta/main.yml b/roles/curl_scheduled/meta/main.yml new file mode 100644 index 0000000..3db64d1 --- /dev/null +++ b/roles/curl_scheduled/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: curl_scheduled + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/curl_scheduled/molecule/default/converge.yml b/roles/curl_scheduled/molecule/default/converge.yml new file mode 100644 index 0000000..14cde54 --- /dev/null +++ b/roles/curl_scheduled/molecule/default/converge.yml @@ -0,0 +1,45 @@ +--- +- name: Converge + hosts: all + + pre_tasks: + - name: "Create test directory" + ansible.builtin.file: + path: "/test" + state: directory + mode: "0664" + + - name: "Create a test file" + ansible.builtin.lineinfile: + path: "/test/index.html" + create: true + mode: "0664" + line: "OK" + + - name: "Simulate remote HTTP server(s) directly on localhost" + changed_when: false + async: 1 + poll: 0 + args: + chdir: "/test" + loop: + - 8080 + - 8081 + - 8082 + ansible.builtin.shell: + cmd: nohup python3 -m http.server {{ item }} /dev/null 2>&1 & + executable: /bin/bash + + roles: + - role: genlab.curl_scheduled + services: + - label: "localhost-test-zero" + url: "http://127.0.0.1:8080" + schedule: minutely + - label: "localhost-test-one" + url: "http://127.0.0.1:8081" + schedule: hourly + - label: "localhost-test-chained-curl" + curl_cmd: '/usr/bin/curl "http://127.0.0.1:8081" && /usr/bin/curl' + url: "http://127.0.0.1:8082" + schedule: minutely diff --git a/roles/curl_scheduled/molecule/default/molecule.yml b/roles/curl_scheduled/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/curl_scheduled/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/curl_scheduled/molecule/default/verify.yml b/roles/curl_scheduled/molecule/default/verify.yml new file mode 100644 index 0000000..66dc803 --- /dev/null +++ b/roles/curl_scheduled/molecule/default/verify.yml @@ -0,0 +1,81 @@ +--- +- name: Verify + hosts: all + gather_facts: false + + tasks: + # Verify the systemd timers + + - name: "Check the timer status - localhost-test-zero" + register: timer_zero + ansible.builtin.systemd: + name: "curl-localhost-test-zero.timer" + + - name: "Assert that the localhost-test-zero timer is running" + ansible.builtin.assert: + that: + - timer_zero.status.ActiveState == "active" + success_msg: "Timer is running" + fail_msg: "Unexpected timer state: '{{ timer_zero.status.ActiveState }}'" + + - name: "Check the timer status - localhost-test-one" + register: timer_one + ansible.builtin.systemd: + name: "curl-localhost-test-one.timer" + + - name: "Assert that the localhost-test-one timer is running" + ansible.builtin.assert: + that: + - timer_one.status.ActiveState == "active" + success_msg: "Timer is running" + fail_msg: "Unexpected timer state: '{{ timer_one.status.ActiveState }}'" + + - name: "Check the timer status - localhost-test-chained-curl" + register: timer_chained_curl + ansible.builtin.systemd: + name: "curl-localhost-test-chained-curl.timer" + + - name: "Assert that the localhost-test-chained-curl timer is running" + ansible.builtin.assert: + that: + - timer_chained_curl.status.ActiveState == "active" + success_msg: "Timer is running" + fail_msg: "Unexpected timer state: '{{ timer_chained_curl.status.ActiveState }}'" + + ## Verify the systemd services + + - name: "Check the service status - zero" + register: service_zero + ansible.builtin.systemd: + name: "curl-localhost-test-zero.service" + + - name: "Assert that the localhost-test-zero service exited with a 0/SUCCESS status" + ansible.builtin.assert: + that: + - 'service_zero.status.ExecMainStatus == "0"' + success_msg: "Service has exited with a 0/SUCCESS status" + fail_msg: "Unexpected service status code: '{{ service_zero.status.ExecMainStatus }}'" + + - name: "Check the service status - one" + register: service_one + ansible.builtin.systemd: + name: "curl-localhost-test-one.service" + + - name: "Assert that the localhost-test-one service exited with a 0/SUCCESS status" + ansible.builtin.assert: + that: + - 'service_one.status.ExecMainStatus == "0"' + success_msg: "Service has exited with a 0/SUCCESS status" + fail_msg: "Unexpected service status code: '{{ service_one.status.ExecMainStatus }}'" + + - name: "Check the service status - localhost-test-chained-curl" + register: service_chained_curl + ansible.builtin.systemd: + name: "curl-localhost-test-chained-curl.service" + + - name: "Assert that the localhost-test-chained-curl service exited with a 0/SUCCESS status" + ansible.builtin.assert: + that: + - 'service_chained_curl.status.ExecMainStatus == "0"' + success_msg: "Service has exited with a 0/SUCCESS status" + fail_msg: "Unexpected service status code: '{{ service_chained_curl.status.ExecMainStatus }}'" diff --git a/roles/curl_scheduled/renovate.json b/roles/curl_scheduled/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/curl_scheduled/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/curl_scheduled/requirements.ci.txt b/roles/curl_scheduled/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/curl_scheduled/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/curl_scheduled/requirements.txt b/roles/curl_scheduled/requirements.txt new file mode 100644 index 0000000..cd252b9 --- /dev/null +++ b/roles/curl_scheduled/requirements.txt @@ -0,0 +1 @@ +ansible~=11.1.0 diff --git a/roles/curl_scheduled/requirements.yml b/roles/curl_scheduled/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/curl_scheduled/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/curl_scheduled/tasks/deploy-service.yml b/roles/curl_scheduled/tasks/deploy-service.yml new file mode 100644 index 0000000..5ed331c --- /dev/null +++ b/roles/curl_scheduled/tasks/deploy-service.yml @@ -0,0 +1,33 @@ +--- +- name: "Deploy-Service | Create systemd service file: '{{ job.label }}'" + vars: + label: "{{ job.label }}" + url: "{{ job.url }}" + curl_cmd: "{{ job.curl_cmd | default(default_curl_cmd) }}" + curl_args: "{{ job.curl_args | default(default_curl_args) }}" + register: service + ansible.builtin.template: + src: "placeholder.service" + dest: "/etc/systemd/system/curl-{{ job.label }}.service" + mode: "0660" + validate: systemd-analyze verify %s + +- name: "Deploy-Service | Create systemd timer file: '{{ job.label }}'" + vars: + label: "{{ job.label }}" + schedule: "{{ job.schedule | default(default_schedule) }}" + register: timer + ansible.builtin.template: + src: "placeholder.timer" + dest: "/etc/systemd/system/curl-{{ job.label }}.timer" + mode: "0660" + validate: systemd-analyze verify %s + +- name: "Deploy-Service | Enable and start the timer: '{{ job.label }}'" # noqa: no-handler + become: true + when: service.changed or timer.changed + ansible.builtin.systemd: + name: "curl-{{ job.label }}.timer" + state: started + enabled: true + daemon_reload: true diff --git a/roles/curl_scheduled/tasks/main.yml b/roles/curl_scheduled/tasks/main.yml new file mode 100644 index 0000000..b0a19de --- /dev/null +++ b/roles/curl_scheduled/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- name: "Install curl (Debian derivatives)" + when: ansible_os_family == "Debian" + ansible.builtin.apt: + name: curl + state: present + update_cache: true + +- name: "Install curl (RHEL derivatives)" + when: ansible_os_family == "RedHat" + ansible.builtin.dnf: + name: curl + state: present + +- name: "Configure and deploy systemd service" + loop: "{{ services }}" + loop_control: + loop_var: "job" + ansible.builtin.include_tasks: "deploy-service.yml" diff --git a/roles/curl_scheduled/templates/placeholder.service b/roles/curl_scheduled/templates/placeholder.service new file mode 100644 index 0000000..e9222cf --- /dev/null +++ b/roles/curl_scheduled/templates/placeholder.service @@ -0,0 +1,21 @@ +[Unit] +Description=Run an HTTP request via curl designated '{{ label }}' +After=network.target + +[Service] +Type=oneshot +ExecStart={{ curl_cmd }} {{ curl_args }} "{{ url }}" +Restart=no + +# Security hardening +ProtectSystem=strict +NoNewPrivileges=true +PrivateTmp=true +ProtectKernelModules=true +ProtectControlGroups=true +ProtectKernelTunables=true +ProtectClock=yes +RestrictSUIDSGID=true + +[Install] +WantedBy=multi-user.targer diff --git a/roles/curl_scheduled/templates/placeholder.timer b/roles/curl_scheduled/templates/placeholder.timer new file mode 100644 index 0000000..0118d10 --- /dev/null +++ b/roles/curl_scheduled/templates/placeholder.timer @@ -0,0 +1,12 @@ +[Unit] +Description=Trigger a curl command designated '{{ label }}' +Requires=curl-{{ label }}.service + +[Timer] +Unit=curl-{{ label }}.service +OnCalendar={{ schedule }} +AccuracySec=1m +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/roles/curl_scheduled/vars/main.yml b/roles/curl_scheduled/vars/main.yml new file mode 100644 index 0000000..e375583 --- /dev/null +++ b/roles/curl_scheduled/vars/main.yml @@ -0,0 +1,2 @@ +--- +kuma_port: "3001" From 328272e12ba0bd72caa80154baaba898c14088fd Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 12:26:03 +0300 Subject: [PATCH 57/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 181aa68..7f97a16 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ - [alertmanager](roles/alertmanager/README.md) - [borgmatic](roles/borgmatic/README.md) +- [curl_scheduled](roles/curl_scheduled/README.md) - [dnsmasq](roles/dnsmasq/README.md) - [grafana](roles/grafana/README.md) - [ipmi_exporter](roles/ipmi_exporter/README.md) diff --git a/galaxy.yml b/galaxy.yml index 4bb7cfd..be7296a 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.11.0 +version: 0.12.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 2e8f73bbf45eca62cb4f4b126973320ec13030b2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 12:38:27 +0300 Subject: [PATCH 58/96] fix ansible-lint errors --- roles/curl_scheduled/defaults/main.yml | 6 ++-- roles/curl_scheduled/meta/main.yml | 4 +-- .../molecule/default/converge.yml | 2 +- .../molecule/default/verify.yml | 36 +++++++++---------- roles/curl_scheduled/tasks/deploy-service.yml | 12 +++---- roles/curl_scheduled/vars/main.yml | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/roles/curl_scheduled/defaults/main.yml b/roles/curl_scheduled/defaults/main.yml index ae69ac0..a6f0e51 100644 --- a/roles/curl_scheduled/defaults/main.yml +++ b/roles/curl_scheduled/defaults/main.yml @@ -1,4 +1,4 @@ --- -default_curl_cmd: "/usr/bin/curl" -default_curl_args: "-fsS -m 10" -default_schedule: "minutely" +curl_scheduled_curl_cmd: "/usr/bin/curl" +curl_scheduled_curl_args: "-fsS -m 10" +curl_scheduled_schedule: "minutely" diff --git a/roles/curl_scheduled/meta/main.yml b/roles/curl_scheduled/meta/main.yml index 3db64d1..894fc30 100644 --- a/roles/curl_scheduled/meta/main.yml +++ b/roles/curl_scheduled/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/curl_scheduled/molecule/default/converge.yml b/roles/curl_scheduled/molecule/default/converge.yml index 14cde54..0445024 100644 --- a/roles/curl_scheduled/molecule/default/converge.yml +++ b/roles/curl_scheduled/molecule/default/converge.yml @@ -31,7 +31,7 @@ executable: /bin/bash roles: - - role: genlab.curl_scheduled + - role: genlab.common.curl_scheduled services: - label: "localhost-test-zero" url: "http://127.0.0.1:8080" diff --git a/roles/curl_scheduled/molecule/default/verify.yml b/roles/curl_scheduled/molecule/default/verify.yml index 66dc803..5ba80ca 100644 --- a/roles/curl_scheduled/molecule/default/verify.yml +++ b/roles/curl_scheduled/molecule/default/verify.yml @@ -7,75 +7,75 @@ # Verify the systemd timers - name: "Check the timer status - localhost-test-zero" - register: timer_zero + register: curl_scheduled_timer_zero ansible.builtin.systemd: name: "curl-localhost-test-zero.timer" - name: "Assert that the localhost-test-zero timer is running" ansible.builtin.assert: that: - - timer_zero.status.ActiveState == "active" + - curl_scheduled_timer_zero.status.ActiveState == "active" success_msg: "Timer is running" - fail_msg: "Unexpected timer state: '{{ timer_zero.status.ActiveState }}'" + fail_msg: "Unexpected timer state: '{{ curl_scheduled_timer_zero.status.ActiveState }}'" - name: "Check the timer status - localhost-test-one" - register: timer_one + register: curl_scheduled_timer_one ansible.builtin.systemd: name: "curl-localhost-test-one.timer" - name: "Assert that the localhost-test-one timer is running" ansible.builtin.assert: that: - - timer_one.status.ActiveState == "active" + - curl_scheduled_timer_one.status.ActiveState == "active" success_msg: "Timer is running" - fail_msg: "Unexpected timer state: '{{ timer_one.status.ActiveState }}'" + fail_msg: "Unexpected timer state: '{{ curl_scheduled_timer_one.status.ActiveState }}'" - name: "Check the timer status - localhost-test-chained-curl" - register: timer_chained_curl + register: curl_scheduled_timer_chained_curl ansible.builtin.systemd: name: "curl-localhost-test-chained-curl.timer" - name: "Assert that the localhost-test-chained-curl timer is running" ansible.builtin.assert: that: - - timer_chained_curl.status.ActiveState == "active" + - curl_scheduled_timer_chained_curl.status.ActiveState == "active" success_msg: "Timer is running" - fail_msg: "Unexpected timer state: '{{ timer_chained_curl.status.ActiveState }}'" + fail_msg: "Unexpected timer state: '{{ curl_scheduled_timer_chained_curl.status.ActiveState }}'" ## Verify the systemd services - name: "Check the service status - zero" - register: service_zero + register: curl_scheduled_service_zero ansible.builtin.systemd: name: "curl-localhost-test-zero.service" - name: "Assert that the localhost-test-zero service exited with a 0/SUCCESS status" ansible.builtin.assert: that: - - 'service_zero.status.ExecMainStatus == "0"' + - 'curl_scheduled_service_zero.status.ExecMainStatus == "0"' success_msg: "Service has exited with a 0/SUCCESS status" - fail_msg: "Unexpected service status code: '{{ service_zero.status.ExecMainStatus }}'" + fail_msg: "Unexpected service status code: '{{ curl_scheduled_service_zero.status.ExecMainStatus }}'" - name: "Check the service status - one" - register: service_one + register: curl_scheduled_service_one ansible.builtin.systemd: name: "curl-localhost-test-one.service" - name: "Assert that the localhost-test-one service exited with a 0/SUCCESS status" ansible.builtin.assert: that: - - 'service_one.status.ExecMainStatus == "0"' + - 'curl_scheduled_service_one.status.ExecMainStatus == "0"' success_msg: "Service has exited with a 0/SUCCESS status" - fail_msg: "Unexpected service status code: '{{ service_one.status.ExecMainStatus }}'" + fail_msg: "Unexpected service status code: '{{ curl_scheduled_service_one.status.ExecMainStatus }}'" - name: "Check the service status - localhost-test-chained-curl" - register: service_chained_curl + register: curl_scheduled_service_chained_curl ansible.builtin.systemd: name: "curl-localhost-test-chained-curl.service" - name: "Assert that the localhost-test-chained-curl service exited with a 0/SUCCESS status" ansible.builtin.assert: that: - - 'service_chained_curl.status.ExecMainStatus == "0"' + - 'curl_scheduled_service_chained_curl.status.ExecMainStatus == "0"' success_msg: "Service has exited with a 0/SUCCESS status" - fail_msg: "Unexpected service status code: '{{ service_chained_curl.status.ExecMainStatus }}'" + fail_msg: "Unexpected service status code: '{{ curl_scheduled_service_chained_curl.status.ExecMainStatus }}'" diff --git a/roles/curl_scheduled/tasks/deploy-service.yml b/roles/curl_scheduled/tasks/deploy-service.yml index 5ed331c..66e4239 100644 --- a/roles/curl_scheduled/tasks/deploy-service.yml +++ b/roles/curl_scheduled/tasks/deploy-service.yml @@ -3,9 +3,9 @@ vars: label: "{{ job.label }}" url: "{{ job.url }}" - curl_cmd: "{{ job.curl_cmd | default(default_curl_cmd) }}" - curl_args: "{{ job.curl_args | default(default_curl_args) }}" - register: service + curl_cmd: "{{ job.curl_cmd | default(curl_scheduled_curl_cmd) }}" + curl_args: "{{ job.curl_args | default(curl_scheduled_curl_args) }}" + register: curl_scheduled_service ansible.builtin.template: src: "placeholder.service" dest: "/etc/systemd/system/curl-{{ job.label }}.service" @@ -15,8 +15,8 @@ - name: "Deploy-Service | Create systemd timer file: '{{ job.label }}'" vars: label: "{{ job.label }}" - schedule: "{{ job.schedule | default(default_schedule) }}" - register: timer + schedule: "{{ job.schedule | default(curl_scheduled_schedule) }}" + register: curl_scheduled_timer ansible.builtin.template: src: "placeholder.timer" dest: "/etc/systemd/system/curl-{{ job.label }}.timer" @@ -25,7 +25,7 @@ - name: "Deploy-Service | Enable and start the timer: '{{ job.label }}'" # noqa: no-handler become: true - when: service.changed or timer.changed + when: curl_scheduled_service.changed or curl_scheduled_timer.changed ansible.builtin.systemd: name: "curl-{{ job.label }}.timer" state: started diff --git a/roles/curl_scheduled/vars/main.yml b/roles/curl_scheduled/vars/main.yml index e375583..01952d4 100644 --- a/roles/curl_scheduled/vars/main.yml +++ b/roles/curl_scheduled/vars/main.yml @@ -1,2 +1,2 @@ --- -kuma_port: "3001" +curl_scheduled_kuma_port: "3001" From f8ee7aaab2cbe4372f26fa34331a32b954a66de3 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 12:39:02 +0300 Subject: [PATCH 59/96] remove garbage --- roles/curl_scheduled/.ansible-lint | 16 ---------------- roles/curl_scheduled/.yamllint | 8 -------- roles/curl_scheduled/conda.dev.yml | 11 ----------- roles/curl_scheduled/conda.prod.yml | 9 --------- roles/curl_scheduled/renovate.json | 6 ------ roles/curl_scheduled/requirements.ci.txt | 6 ------ roles/curl_scheduled/requirements.txt | 1 - roles/curl_scheduled/requirements.yml | 3 --- 8 files changed, 60 deletions(-) delete mode 100644 roles/curl_scheduled/.ansible-lint delete mode 100644 roles/curl_scheduled/.yamllint delete mode 100644 roles/curl_scheduled/conda.dev.yml delete mode 100644 roles/curl_scheduled/conda.prod.yml delete mode 100644 roles/curl_scheduled/renovate.json delete mode 100644 roles/curl_scheduled/requirements.ci.txt delete mode 100644 roles/curl_scheduled/requirements.txt delete mode 100644 roles/curl_scheduled/requirements.yml diff --git a/roles/curl_scheduled/.ansible-lint b/roles/curl_scheduled/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/curl_scheduled/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/curl_scheduled/.yamllint b/roles/curl_scheduled/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/curl_scheduled/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/curl_scheduled/conda.dev.yml b/roles/curl_scheduled/conda.dev.yml deleted file mode 100644 index c887590..0000000 --- a/roles/curl_scheduled/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-curl-scheduled -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/curl_scheduled/conda.prod.yml b/roles/curl_scheduled/conda.prod.yml deleted file mode 100644 index ab2ae6a..0000000 --- a/roles/curl_scheduled/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-curl-scheduled -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/curl_scheduled/renovate.json b/roles/curl_scheduled/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/curl_scheduled/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/curl_scheduled/requirements.ci.txt b/roles/curl_scheduled/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/curl_scheduled/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/curl_scheduled/requirements.txt b/roles/curl_scheduled/requirements.txt deleted file mode 100644 index cd252b9..0000000 --- a/roles/curl_scheduled/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.1.0 diff --git a/roles/curl_scheduled/requirements.yml b/roles/curl_scheduled/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/curl_scheduled/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From dd24220532a3a06d59385f6bd5829a46f5aef02d Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:42:21 +0300 Subject: [PATCH 60/96] add karma role and fix lint errors --- roles/karma/.ansible-lint | 16 ++++++ roles/karma/.gitignore | 2 + roles/karma/.yamllint | 8 +++ roles/karma/README.md | 53 ++++++++++++++++++ roles/karma/conda.dev.yml | 11 ++++ roles/karma/conda.prod.yml | 9 ++++ roles/karma/defaults/main.yml | 7 +++ roles/karma/handlers/main.yml | 7 +++ roles/karma/meta/main.yml | 17 ++++++ roles/karma/molecule/default/converge.yml | 9 ++++ roles/karma/molecule/default/karma.conf | 17 ++++++ roles/karma/molecule/default/molecule.yml | 27 ++++++++++ roles/karma/molecule/default/verify.yml | 35 ++++++++++++ roles/karma/renovate.json | 6 +++ roles/karma/requirements.ci.txt | 6 +++ roles/karma/requirements.txt | 1 + roles/karma/requirements.yml | 3 ++ roles/karma/tasks/configuration.yml | 10 ++++ roles/karma/tasks/install.yml | 66 +++++++++++++++++++++++ roles/karma/tasks/main.yml | 9 ++++ roles/karma/templates/karma.service.j2 | 26 +++++++++ roles/karma/vars/main.yml | 1 + 22 files changed, 346 insertions(+) create mode 100644 roles/karma/.ansible-lint create mode 100644 roles/karma/.gitignore create mode 100644 roles/karma/.yamllint create mode 100644 roles/karma/README.md create mode 100644 roles/karma/conda.dev.yml create mode 100644 roles/karma/conda.prod.yml create mode 100644 roles/karma/defaults/main.yml create mode 100644 roles/karma/handlers/main.yml create mode 100644 roles/karma/meta/main.yml create mode 100644 roles/karma/molecule/default/converge.yml create mode 100644 roles/karma/molecule/default/karma.conf create mode 100644 roles/karma/molecule/default/molecule.yml create mode 100644 roles/karma/molecule/default/verify.yml create mode 100644 roles/karma/renovate.json create mode 100644 roles/karma/requirements.ci.txt create mode 100644 roles/karma/requirements.txt create mode 100644 roles/karma/requirements.yml create mode 100644 roles/karma/tasks/configuration.yml create mode 100644 roles/karma/tasks/install.yml create mode 100644 roles/karma/tasks/main.yml create mode 100644 roles/karma/templates/karma.service.j2 create mode 100644 roles/karma/vars/main.yml diff --git a/roles/karma/.ansible-lint b/roles/karma/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/karma/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/karma/.gitignore b/roles/karma/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/karma/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/karma/.yamllint b/roles/karma/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/karma/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/karma/README.md b/roles/karma/README.md new file mode 100644 index 0000000..3282de9 --- /dev/null +++ b/roles/karma/README.md @@ -0,0 +1,53 @@ +genlab.karma +========= +This is the ansible role to install and configure Karma - alert dashboard for Prometheus Alertmanager (https://github.com/prymitive/karma) + +------------ +⚠️ Do not forget to update: + +- `meta/main.yml` +- Conda/Mamba manifests +- this README =) including *the name at the top* and *maintainers*. + +Requirements +------------ + +None + +Role Variables +-------------- +```yaml +karma_version: "0.121" - karma version +karma_user: karma - name of karma system user +karma_group: karma - name of karma system group +karma_dir: /etc/karma - path where to unpack karma +karma_config_dir: /etc/karma/conf - where to copy configuration file +config_source_dir: source - path to source dir with karma config on localhost +``` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.karma + karma_version: "0.121" + config_source_dir: "karma/" + karma_dir: "/etc/karma" + karma_config_dir: "/etc/karma/config" +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/karma/conda.dev.yml b/roles/karma/conda.dev.yml new file mode 100644 index 0000000..fb974b7 --- /dev/null +++ b/roles/karma/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/karma/conda.prod.yml b/roles/karma/conda.prod.yml new file mode 100644 index 0000000..d6d12d6 --- /dev/null +++ b/roles/karma/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-role-template +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/karma/defaults/main.yml b/roles/karma/defaults/main.yml new file mode 100644 index 0000000..f6c4844 --- /dev/null +++ b/roles/karma/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# Default variables for Karma role +karma_version: "0.121" +karma_user: karma +karma_group: karma +karma_dir: /etc/karma +karma_config_dir: /etc/karma/conf diff --git a/roles/karma/handlers/main.yml b/roles/karma/handlers/main.yml new file mode 100644 index 0000000..d7748d6 --- /dev/null +++ b/roles/karma/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "(Re)start and enable karma" + ansible.builtin.systemd_service: + name: karma.service + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/karma/meta/main.yml b/roles/karma/meta/main.yml new file mode 100644 index 0000000..87eb400 --- /dev/null +++ b/roles/karma/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "karma" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy", "noble"] + + galaxy_tags: [monitoring] + +dependencies: [] diff --git a/roles/karma/molecule/default/converge.yml b/roles/karma/molecule/default/converge.yml new file mode 100644 index 0000000..6f4bd54 --- /dev/null +++ b/roles/karma/molecule/default/converge.yml @@ -0,0 +1,9 @@ +--- +- name: Converge + hosts: all + gather_facts: true + become: true + roles: + - role: genlab.common.karma + karma_version: "0.121" + config_source_dir: molecule/default/ diff --git a/roles/karma/molecule/default/karma.conf b/roles/karma/molecule/default/karma.conf new file mode 100644 index 0000000..8956068 --- /dev/null +++ b/roles/karma/molecule/default/karma.conf @@ -0,0 +1,17 @@ +alertmanager: + interval: 1m + servers: + - name: production + uri: http://localhost:9093 + timeout: 20s + proxy: false + readonly: true +listen: + address: "0.0.0.0" + port: 8080 + prefix: / + tls: + cert: "" + key: "" + cors: + allowedOrigins: [] \ No newline at end of file diff --git a/roles/karma/molecule/default/molecule.yml b/roles/karma/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/karma/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/karma/molecule/default/verify.yml b/roles/karma/molecule/default/verify.yml new file mode 100644 index 0000000..d38c99c --- /dev/null +++ b/roles/karma/molecule/default/verify.yml @@ -0,0 +1,35 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + karma_version: "0.121" + + tasks: + - name: "Check if Karma is installed" + changed_when: false + ansible.builtin.command: "karma --version" + register: karma_installed_version + + - name: "Check Karma version" + ansible.builtin.assert: + that: "karma_installed_version.stdout is regex('{{ karma_version }}')" + success_msg: "Karma version {{ karma_version }} is installed and working" + fail_msg: "Karma version {{ karma_version }} is not installed or not working correctly" + + # kics-scan ignore-block + - name: "Check if Karma is reachable" + ansible.builtin.uri: + url: "http://localhost:8080/health" + return_content: true + status_code: 200 + method: GET + body_format: json + register: karma_health + + - name: "Assert Karma health status" + ansible.builtin.assert: + that: "karma_health.content.strip() == 'Pong'" + success_msg: "Karma is healthy" + fail_msg: "Karma is not healthy" diff --git a/roles/karma/renovate.json b/roles/karma/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/karma/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/karma/requirements.ci.txt b/roles/karma/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/karma/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/karma/requirements.txt b/roles/karma/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/karma/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/karma/requirements.yml b/roles/karma/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/karma/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/karma/tasks/configuration.yml b/roles/karma/tasks/configuration.yml new file mode 100644 index 0000000..e0a7400 --- /dev/null +++ b/roles/karma/tasks/configuration.yml @@ -0,0 +1,10 @@ +--- + +- name: Create karma configuration file + notify: "(Re)start and enable karma" + ansible.builtin.template: + src: "{{ config_source_dir }}/karma.conf" + dest: "{{ karma_config_dir }}/karma.conf" + owner: "{{ karma_user }}" + group: "{{ karma_group }}" + mode: '0640' diff --git a/roles/karma/tasks/install.yml b/roles/karma/tasks/install.yml new file mode 100644 index 0000000..3cc4e4c --- /dev/null +++ b/roles/karma/tasks/install.yml @@ -0,0 +1,66 @@ +--- + +- name: "Create Karma system group" + ansible.builtin.group: + name: "{{ karma_user }}" + system: true + state: present + +- name: "Create karma system user" + ansible.builtin.user: + name: "{{ karma_user }}" + group: "{{ karma_group }}" + system: true + shell: "/sbin/nologin" + create_home: false + state: present + +- name: "Install karma from binary" + block: + - name: "Check karma version" + changed_when: false + ansible.builtin.command: + cmd: "{{ karma_dir }}/karma-linux-amd64 --version" + register: karma_ver + + - name: "Assert version correctness" + ansible.builtin.assert: + that: "karma_ver.stdout is regex('{{ karma_version }}')" + success_msg: "karma version {{ karma_version }} is installed and working" + fail_msg: "karma version {{ karma_version }} is not installed or not working correctly" + + rescue: + - name: "Create karma directories" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ karma_user }}" + group: "{{ karma_group }}" + mode: "0755" + with_items: + - "{{ karma_dir }}" + - "{{ karma_config_dir }}" + + - name: "Unarchive karma tar file" + notify: "(Re)start and enable karma" + ansible.builtin.unarchive: + src: "https://github.com/prymitive/karma/releases/download/v{{ karma_version }}/karma-linux-amd64.tar.gz" + dest: "{{ karma_dir }}" + remote_src: true + + - name: "Move karma binary" + ansible.builtin.copy: + src: "{{ karma_dir }}/karma-linux-amd64" + dest: "/usr/local/bin/karma" + mode: "0755" + owner: "{{ karma_user }}" + group: "{{ karma_group }}" + remote_src: true + +- name: Create systemd service file + ansible.builtin.template: + src: karma.service.j2 + dest: /etc/systemd/system/karma.service + owner: "{{ karma_user }}" + group: "{{ karma_group }}" + mode: '0644' diff --git a/roles/karma/tasks/main.yml b/roles/karma/tasks/main.yml new file mode 100644 index 0000000..6bd62b2 --- /dev/null +++ b/roles/karma/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: "Run installation tasks" + ansible.builtin.include_tasks: install.yml + +- name: "Run configuration tasks" + ansible.builtin.include_tasks: configuration.yml + +- name: "Flush handlers" + ansible.builtin.meta: "flush_handlers" diff --git a/roles/karma/templates/karma.service.j2 b/roles/karma/templates/karma.service.j2 new file mode 100644 index 0000000..312fac8 --- /dev/null +++ b/roles/karma/templates/karma.service.j2 @@ -0,0 +1,26 @@ +[Unit] +Description=Karma - Alertmanager dashboard +After=network.target +Documentation=https://github.com/prymitive/karma + +[Service] +Type=simple +User={{ karma_user }} +Group={{ karma_group }} +ExecStart=karma --config.file={{ karma_config_dir }}/karma.conf +Restart=always +RestartSec=5 +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=karma +ProtectSystem=strict +NoNewPrivileges=true +PrivateTmp=true +ProtectKernelModules=true +ProtectControlGroups=true +ProtectKernelTunables=true +ProtectClock=yes +RestrictSUIDSGID=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/roles/karma/vars/main.yml b/roles/karma/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/karma/vars/main.yml @@ -0,0 +1 @@ +--- From 2b4b8e5d258957d2155d2a27a4a6255a793138d2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:43:00 +0300 Subject: [PATCH 61/96] remove garbage --- roles/karma/.ansible-lint | 16 ---------------- roles/karma/.yamllint | 8 -------- roles/karma/conda.dev.yml | 11 ----------- roles/karma/conda.prod.yml | 9 --------- roles/karma/renovate.json | 6 ------ roles/karma/requirements.ci.txt | 6 ------ roles/karma/requirements.txt | 1 - roles/karma/requirements.yml | 3 --- 8 files changed, 60 deletions(-) delete mode 100644 roles/karma/.ansible-lint delete mode 100644 roles/karma/.yamllint delete mode 100644 roles/karma/conda.dev.yml delete mode 100644 roles/karma/conda.prod.yml delete mode 100644 roles/karma/renovate.json delete mode 100644 roles/karma/requirements.ci.txt delete mode 100644 roles/karma/requirements.txt delete mode 100644 roles/karma/requirements.yml diff --git a/roles/karma/.ansible-lint b/roles/karma/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/karma/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/karma/.yamllint b/roles/karma/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/karma/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/karma/conda.dev.yml b/roles/karma/conda.dev.yml deleted file mode 100644 index fb974b7..0000000 --- a/roles/karma/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/karma/conda.prod.yml b/roles/karma/conda.prod.yml deleted file mode 100644 index d6d12d6..0000000 --- a/roles/karma/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-role-template -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/karma/renovate.json b/roles/karma/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/karma/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/karma/requirements.ci.txt b/roles/karma/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/karma/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/karma/requirements.txt b/roles/karma/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/karma/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 diff --git a/roles/karma/requirements.yml b/roles/karma/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/karma/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From 64a0db45f90a24479e3c43337e2a50206c78fd10 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:43:58 +0300 Subject: [PATCH 62/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f97a16..c52e497 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - [dnsmasq](roles/dnsmasq/README.md) - [grafana](roles/grafana/README.md) - [ipmi_exporter](roles/ipmi_exporter/README.md) +- [karma](roles/karma/README.md) - [mount_device](roles/mount_device/README.md) - [prometheus](roles/prometheus/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) diff --git a/galaxy.yml b/galaxy.yml index be7296a..ad66e79 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.12.0 +version: 0.13.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 433f22dca0af72a8d4e21162c345ff5d0a2fd633 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:49:01 +0300 Subject: [PATCH 63/96] add nginx role --- roles/nginx/.ansible-lint | 16 ++++ roles/nginx/.gitignore | 2 + roles/nginx/.yamllint | 8 ++ roles/nginx/README.md | 37 ++++++++++ roles/nginx/conda.dev.yml | 11 +++ roles/nginx/conda.prod.yml | 9 +++ roles/nginx/defaults/main.yml | 2 + roles/nginx/handlers/main.yml | 16 ++++ roles/nginx/meta/main.yml | 17 +++++ roles/nginx/molecule/default/converge.yml | 36 +++++++++ roles/nginx/molecule/default/files/index.html | 1 + roles/nginx/molecule/default/molecule.yml | 27 +++++++ .../default/site-configs/test-dir.conf | 9 +++ roles/nginx/molecule/default/verify.yml | 73 +++++++++++++++++++ roles/nginx/renovate.json | 6 ++ roles/nginx/requirements.ci.txt | 6 ++ roles/nginx/requirements.txt | 1 + roles/nginx/requirements.yml | 3 + roles/nginx/tasks/main.yml | 53 ++++++++++++++ roles/nginx/vars/main.yml | 1 + 20 files changed, 334 insertions(+) create mode 100644 roles/nginx/.ansible-lint create mode 100644 roles/nginx/.gitignore create mode 100644 roles/nginx/.yamllint create mode 100644 roles/nginx/README.md create mode 100644 roles/nginx/conda.dev.yml create mode 100644 roles/nginx/conda.prod.yml create mode 100644 roles/nginx/defaults/main.yml create mode 100644 roles/nginx/handlers/main.yml create mode 100644 roles/nginx/meta/main.yml create mode 100644 roles/nginx/molecule/default/converge.yml create mode 100644 roles/nginx/molecule/default/files/index.html create mode 100644 roles/nginx/molecule/default/molecule.yml create mode 100644 roles/nginx/molecule/default/site-configs/test-dir.conf create mode 100644 roles/nginx/molecule/default/verify.yml create mode 100644 roles/nginx/renovate.json create mode 100644 roles/nginx/requirements.ci.txt create mode 100644 roles/nginx/requirements.txt create mode 100644 roles/nginx/requirements.yml create mode 100644 roles/nginx/tasks/main.yml create mode 100644 roles/nginx/vars/main.yml diff --git a/roles/nginx/.ansible-lint b/roles/nginx/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/nginx/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/nginx/.gitignore b/roles/nginx/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/nginx/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/nginx/.yamllint b/roles/nginx/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/nginx/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/nginx/README.md b/roles/nginx/README.md new file mode 100644 index 0000000..9ce29f0 --- /dev/null +++ b/roles/nginx/README.md @@ -0,0 +1,37 @@ +ansible-nginx +========= + +Deploy NGINX with a minimal configuration. + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.nginx +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/nginx/conda.dev.yml b/roles/nginx/conda.dev.yml new file mode 100644 index 0000000..2b3ef23 --- /dev/null +++ b/roles/nginx/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-nginx +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/nginx/conda.prod.yml b/roles/nginx/conda.prod.yml new file mode 100644 index 0000000..e401511 --- /dev/null +++ b/roles/nginx/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-nginx +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml new file mode 100644 index 0000000..b9f6c1f --- /dev/null +++ b/roles/nginx/defaults/main.yml @@ -0,0 +1,2 @@ +--- +nginx_version: "1.26.3" diff --git a/roles/nginx/handlers/main.yml b/roles/nginx/handlers/main.yml new file mode 100644 index 0000000..aaf83c0 --- /dev/null +++ b/roles/nginx/handlers/main.yml @@ -0,0 +1,16 @@ +--- +- name: "Update apt cache" + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 + +- name: "Start nginx" + ansible.builtin.service: + name: nginx + state: started + enabled: true + +- name: "Reload nginx" + ansible.builtin.service: + name: nginx + state: reloaded diff --git a/roles/nginx/meta/main.yml b/roles/nginx/meta/main.yml new file mode 100644 index 0000000..33b67c1 --- /dev/null +++ b/roles/nginx/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "nginx" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/nginx/molecule/default/converge.yml b/roles/nginx/molecule/default/converge.yml new file mode 100644 index 0000000..c6034a0 --- /dev/null +++ b/roles/nginx/molecule/default/converge.yml @@ -0,0 +1,36 @@ +--- +- name: Converge + hosts: all + post_tasks: + - name: "Create a test site root" + ansible.builtin.file: + path: /var/www/html + state: directory + owner: nginx + group: nginx + mode: "0755" + + - name: "Push a test index file" + ansible.builtin.copy: + src: index.html + dest: /var/www/html + owner: nginx + group: nginx + mode: "0644" + + roles: + # using default NGINX verison + - role: genlab.nginx + site: + confname: test-var + content: | + server { + listen 80; + server_name example.com www.example.com; + + location / { + root /var/www/html; + index index.html; + } + } + dir_sites: '{{ lookup("env", "MOLECULE_PROJECT_DIRECTORY") }}/molecule/default/site-configs/' diff --git a/roles/nginx/molecule/default/files/index.html b/roles/nginx/molecule/default/files/index.html new file mode 100644 index 0000000..19d993f --- /dev/null +++ b/roles/nginx/molecule/default/files/index.html @@ -0,0 +1 @@ +Hello from ansible-nginx! diff --git a/roles/nginx/molecule/default/molecule.yml b/roles/nginx/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/nginx/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/nginx/molecule/default/site-configs/test-dir.conf b/roles/nginx/molecule/default/site-configs/test-dir.conf new file mode 100644 index 0000000..0830bee --- /dev/null +++ b/roles/nginx/molecule/default/site-configs/test-dir.conf @@ -0,0 +1,9 @@ +server { + listen 8080; + server_name test.com; + + location / { + root /var/www/html; + index index.html; + } +} diff --git a/roles/nginx/molecule/default/verify.yml b/roles/nginx/molecule/default/verify.yml new file mode 100644 index 0000000..416523b --- /dev/null +++ b/roles/nginx/molecule/default/verify.yml @@ -0,0 +1,73 @@ +# kics-scan disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + pre_tasks: + - name: "Include default vars" + ansible.builtin.include_vars: + dir: '{{ lookup("env", "MOLECULE_PROJECT_DIRECTORY") }}/defaults/' + extensions: + - 'yml' + + tasks: + - name: "Verify NGINX version" + register: nginx_version_output + changed_when: false + failed_when: nginx_version_output.rc != 0 + ansible.builtin.command: /usr/sbin/nginx -v + + # version is displayed as e.g. 'nginx version: nginx/1.26.3' + - name: "Extract NGINX version" + ansible.builtin.set_fact: + nginx_version: "{{ nginx_version_output.stderr.split('/')[1] }}" + + - name: "Check expected NGINX version" + ansible.builtin.assert: + that: nginx_version.startswith(nginx_version) + fail_msg: "Unexpected NGINX version found: '{{ nginx_version }}'" + + - name: "Gather service facts" + ansible.builtin.service_facts: + + - name: "Assert NGINX service is running" + ansible.builtin.assert: + that: ansible_facts.services['nginx.service'].state == 'running' + fail_msg: "NGINX service is not running." + + - name: "Assert NGINX service is enabled" + ansible.builtin.assert: + that: ansible_facts.services['nginx.service'].status == 'enabled' + fail_msg: "NGINX service is not enabled." + + - name: "Get an HTTP response from the test site (single site deployment from a variable)" + register: response + ansible.builtin.uri: + url: http://localhost:80 + status_code: 200 + return_content: true + + - name: "Check the response correctness" + vars: + message: "{{ response.content | trim }}" + ansible.builtin.assert: + that: message == 'Hello from ansible-nginx!' + success_msg: "{{ message }}" + fail_msg: "Unexpected response from the test site: '{{ message }}'" + + - name: "Get an HTTP response from the test site (site deployment from a dictionary)" + register: response + ansible.builtin.uri: + url: http://localhost:8080 + status_code: 200 + return_content: true + + - name: "Check the response correctness" + vars: + message: "{{ response.content | trim }}" + ansible.builtin.assert: + that: message == 'Hello from ansible-nginx!' + success_msg: "{{ message }}" + fail_msg: "Unexpected response from the test site: '{{ message }}'" diff --git a/roles/nginx/renovate.json b/roles/nginx/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/nginx/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/nginx/requirements.ci.txt b/roles/nginx/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/nginx/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/nginx/requirements.txt b/roles/nginx/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/nginx/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/nginx/requirements.yml b/roles/nginx/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/nginx/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml new file mode 100644 index 0000000..b0bfb81 --- /dev/null +++ b/roles/nginx/tasks/main.yml @@ -0,0 +1,53 @@ +--- +- name: "Install gpg-agent" + ansible.builtin.apt: + name: gpg-agent + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Add NGINX signing key" + ansible.builtin.apt_key: + url: https://nginx.org/keys/nginx_signing.key + state: present + +- name: "Add NGINX repository" + notify: "Update apt cache" + ansible.builtin.apt_repository: + repo: "deb http://nginx.org/packages/{{ ansible_distribution | lower }}/ {{ ansible_distribution_release }} nginx" + state: present + +- name: "Install specific NGINX version" + notify: "Start nginx" + ansible.builtin.apt: + name: "nginx={{ nginx_version }}*" + state: present + +- name: "Remove default site configuration if it exists" + notify: "Reload nginx" + ansible.builtin.file: + path: /etc/nginx/conf.d/default.conf + state: absent + +- name: "Deploy a single site configuration" + when: site is defined + notify: "Reload nginx" + ansible.builtin.copy: + dest: "/etc/nginx/conf.d/{{ site.confname }}.conf" + owner: root + group: root + mode: "0644" + content: "{{ site.content }}" + +- name: "Deploy custom site configurations" + when: dir_sites is defined + with_fileglob: + - "{{ dir_sites }}/*.conf" + notify: "Reload nginx" + ansible.builtin.copy: + src: "{{ item }}" + dest: "/etc/nginx/conf.d/{{ item | basename }}" + owner: root + group: root + mode: '0644' + # validate: '/usr/sbin/nginx -t -c %s' diff --git a/roles/nginx/vars/main.yml b/roles/nginx/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/nginx/vars/main.yml @@ -0,0 +1 @@ +--- From fb8d538447653cbd2e2864051123aca796c6dfab Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:51:03 +0300 Subject: [PATCH 64/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c52e497..ecfc21b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - [ipmi_exporter](roles/ipmi_exporter/README.md) - [karma](roles/karma/README.md) - [mount_device](roles/mount_device/README.md) +- [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) - [ufw](roles/ufw/README.md) diff --git a/galaxy.yml b/galaxy.yml index ad66e79..1b2fd3b 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.13.0 +version: 0.14.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 3b7694d7e54a2ca9a20f3171a17368ddabda0bdc Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:58:41 +0300 Subject: [PATCH 65/96] fix linter errors --- roles/nginx/meta/main.yml | 4 ++-- roles/nginx/molecule/default/converge.yml | 2 +- roles/nginx/molecule/default/verify.yml | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/roles/nginx/meta/main.yml b/roles/nginx/meta/main.yml index 33b67c1..b6e9df6 100644 --- a/roles/nginx/meta/main.yml +++ b/roles/nginx/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/nginx/molecule/default/converge.yml b/roles/nginx/molecule/default/converge.yml index c6034a0..30c3718 100644 --- a/roles/nginx/molecule/default/converge.yml +++ b/roles/nginx/molecule/default/converge.yml @@ -20,7 +20,7 @@ roles: # using default NGINX verison - - role: genlab.nginx + - role: genlab.common.nginx site: confname: test-var content: | diff --git a/roles/nginx/molecule/default/verify.yml b/roles/nginx/molecule/default/verify.yml index 416523b..23748b8 100644 --- a/roles/nginx/molecule/default/verify.yml +++ b/roles/nginx/molecule/default/verify.yml @@ -43,30 +43,30 @@ fail_msg: "NGINX service is not enabled." - name: "Get an HTTP response from the test site (single site deployment from a variable)" - register: response + register: nginx_response ansible.builtin.uri: url: http://localhost:80 status_code: 200 return_content: true - - name: "Check the response correctness" + - name: "Check the response correctness on port 80" vars: - message: "{{ response.content | trim }}" + message: "{{ nginx_response.content | trim }}" ansible.builtin.assert: that: message == 'Hello from ansible-nginx!' success_msg: "{{ message }}" fail_msg: "Unexpected response from the test site: '{{ message }}'" - name: "Get an HTTP response from the test site (site deployment from a dictionary)" - register: response + register: nginx_response ansible.builtin.uri: url: http://localhost:8080 status_code: 200 return_content: true - - name: "Check the response correctness" + - name: "Check the response correctness on port 8080" vars: - message: "{{ response.content | trim }}" + message: "{{ nginx_response.content | trim }}" ansible.builtin.assert: that: message == 'Hello from ansible-nginx!' success_msg: "{{ message }}" From 181bab2254dc6263017d434ae0fec5f948d8bd5f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 13:59:07 +0300 Subject: [PATCH 66/96] remove garbage --- roles/nginx/.ansible-lint | 16 ---------------- roles/nginx/.yamllint | 8 -------- roles/nginx/conda.dev.yml | 11 ----------- roles/nginx/conda.prod.yml | 9 --------- roles/nginx/renovate.json | 6 ------ roles/nginx/requirements.ci.txt | 6 ------ roles/nginx/requirements.txt | 1 - roles/nginx/requirements.yml | 3 --- 8 files changed, 60 deletions(-) delete mode 100644 roles/nginx/.ansible-lint delete mode 100644 roles/nginx/.yamllint delete mode 100644 roles/nginx/conda.dev.yml delete mode 100644 roles/nginx/conda.prod.yml delete mode 100644 roles/nginx/renovate.json delete mode 100644 roles/nginx/requirements.ci.txt delete mode 100644 roles/nginx/requirements.txt delete mode 100644 roles/nginx/requirements.yml diff --git a/roles/nginx/.ansible-lint b/roles/nginx/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/nginx/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/nginx/.yamllint b/roles/nginx/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/nginx/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/nginx/conda.dev.yml b/roles/nginx/conda.dev.yml deleted file mode 100644 index 2b3ef23..0000000 --- a/roles/nginx/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-nginx -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/nginx/conda.prod.yml b/roles/nginx/conda.prod.yml deleted file mode 100644 index e401511..0000000 --- a/roles/nginx/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-nginx -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/nginx/renovate.json b/roles/nginx/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/nginx/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/nginx/requirements.ci.txt b/roles/nginx/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/nginx/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/nginx/requirements.txt b/roles/nginx/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/nginx/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 diff --git a/roles/nginx/requirements.yml b/roles/nginx/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/nginx/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From 18fb8ad52aaa95011d25f12aacb7c2edcc542857 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 14:05:16 +0300 Subject: [PATCH 67/96] add rclone_yandex role --- roles/rclone_yandex/.ansible-lint | 16 +++++ roles/rclone_yandex/.gitignore | 2 + roles/rclone_yandex/.yamllint | 8 +++ roles/rclone_yandex/README.md | 41 +++++++++++++ roles/rclone_yandex/defaults/main.yml | 1 + roles/rclone_yandex/environment.ci.yml | 11 ++++ roles/rclone_yandex/environment.yml | 9 +++ roles/rclone_yandex/handlers/main.yml | 1 + roles/rclone_yandex/meta/main.yml | 17 ++++++ .../molecule/default/converge.yml | 19 ++++++ .../molecule/default/molecule.yml | 27 +++++++++ .../molecule/default/secrets/token.yml | 5 ++ .../rclone_yandex/molecule/default/verify.yml | 18 ++++++ roles/rclone_yandex/renovate.json | 6 ++ roles/rclone_yandex/requirements.ci.txt | 6 ++ roles/rclone_yandex/requirements.txt | 1 + roles/rclone_yandex/requirements.yml | 3 + roles/rclone_yandex/tasks/configure.yaml | 58 +++++++++++++++++++ roles/rclone_yandex/tasks/install.yaml | 39 +++++++++++++ roles/rclone_yandex/tasks/main.yml | 11 ++++ roles/rclone_yandex/tasks/run.yaml | 6 ++ .../templates/rclone-yandex.service.j2 | 26 +++++++++ roles/rclone_yandex/vars/main.yml | 4 ++ 23 files changed, 335 insertions(+) create mode 100644 roles/rclone_yandex/.ansible-lint create mode 100644 roles/rclone_yandex/.gitignore create mode 100644 roles/rclone_yandex/.yamllint create mode 100644 roles/rclone_yandex/README.md create mode 100644 roles/rclone_yandex/defaults/main.yml create mode 100644 roles/rclone_yandex/environment.ci.yml create mode 100644 roles/rclone_yandex/environment.yml create mode 100644 roles/rclone_yandex/handlers/main.yml create mode 100644 roles/rclone_yandex/meta/main.yml create mode 100644 roles/rclone_yandex/molecule/default/converge.yml create mode 100644 roles/rclone_yandex/molecule/default/molecule.yml create mode 100644 roles/rclone_yandex/molecule/default/secrets/token.yml create mode 100644 roles/rclone_yandex/molecule/default/verify.yml create mode 100644 roles/rclone_yandex/renovate.json create mode 100644 roles/rclone_yandex/requirements.ci.txt create mode 100644 roles/rclone_yandex/requirements.txt create mode 100644 roles/rclone_yandex/requirements.yml create mode 100644 roles/rclone_yandex/tasks/configure.yaml create mode 100644 roles/rclone_yandex/tasks/install.yaml create mode 100644 roles/rclone_yandex/tasks/main.yml create mode 100644 roles/rclone_yandex/tasks/run.yaml create mode 100644 roles/rclone_yandex/templates/rclone-yandex.service.j2 create mode 100644 roles/rclone_yandex/vars/main.yml diff --git a/roles/rclone_yandex/.ansible-lint b/roles/rclone_yandex/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/rclone_yandex/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/rclone_yandex/.gitignore b/roles/rclone_yandex/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/rclone_yandex/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/rclone_yandex/.yamllint b/roles/rclone_yandex/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/rclone_yandex/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/rclone_yandex/README.md b/roles/rclone_yandex/README.md new file mode 100644 index 0000000..8e6f453 --- /dev/null +++ b/roles/rclone_yandex/README.md @@ -0,0 +1,41 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +`rclone_config_path`: where to keep the configuration file (default: `/root/rclone.conf`) +`rclone_mountpoint`: where to mount the Yandex Disk share +`rclone_group`: how to name the group that will own the mountpoint + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +```yaml +roles: + - role: rclone_yandex + rclone_mountpoint: /mnt/yandex-disk + rclone_group: yandex +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me \ No newline at end of file diff --git a/roles/rclone_yandex/defaults/main.yml b/roles/rclone_yandex/defaults/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/rclone_yandex/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/rclone_yandex/environment.ci.yml b/roles/rclone_yandex/environment.ci.yml new file mode 100644 index 0000000..1c6d7b1 --- /dev/null +++ b/roles/rclone_yandex/environment.ci.yml @@ -0,0 +1,11 @@ +--- +name: ansible-rclone-yandex +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=22.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/rclone_yandex/environment.yml b/roles/rclone_yandex/environment.yml new file mode 100644 index 0000000..b86b81f --- /dev/null +++ b/roles/rclone_yandex/environment.yml @@ -0,0 +1,9 @@ +--- +name: ansible-rclone-yandex +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=22.2 + - pip: + - -r requirements.txt diff --git a/roles/rclone_yandex/handlers/main.yml b/roles/rclone_yandex/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/rclone_yandex/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/rclone_yandex/meta/main.yml b/roles/rclone_yandex/meta/main.yml new file mode 100644 index 0000000..94fef93 --- /dev/null +++ b/roles/rclone_yandex/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "rclone_yandex" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Deploy rclone as a systemd unit and mount a Yandex Disk storage" + license: "GPL-2.0-or-later" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/rclone_yandex/molecule/default/converge.yml b/roles/rclone_yandex/molecule/default/converge.yml new file mode 100644 index 0000000..afa49dd --- /dev/null +++ b/roles/rclone_yandex/molecule/default/converge.yml @@ -0,0 +1,19 @@ +--- +- name: Converge + hosts: all + + pre_tasks: + - name: "Install basic tools" + ansible.builtin.apt: + name: + - curl + update_cache: true + cache_valid_time: 3600 + autoremove: true + autoclean: true + + roles: + - role: genlab.rclone_yandex + rclone_mountpoint: /mnt/yandex-disk + rclone_group: yandex + rclone_secrets: secrets/token.yml diff --git a/roles/rclone_yandex/molecule/default/molecule.yml b/roles/rclone_yandex/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/rclone_yandex/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/rclone_yandex/molecule/default/secrets/token.yml b/roles/rclone_yandex/molecule/default/secrets/token.yml new file mode 100644 index 0000000..0b0550a --- /dev/null +++ b/roles/rclone_yandex/molecule/default/secrets/token.yml @@ -0,0 +1,5 @@ +rclone: + access_token: "y0_Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx..." + token_type: "OAuth" + refresh_token: "1:SPUlxxxxxxxxxxxxxxxxxxxxxxxx..." + expiry: "2024-05-16T05:26:26.319196346Z" diff --git a/roles/rclone_yandex/molecule/default/verify.yml b/roles/rclone_yandex/molecule/default/verify.yml new file mode 100644 index 0000000..4868f6d --- /dev/null +++ b/roles/rclone_yandex/molecule/default/verify.yml @@ -0,0 +1,18 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + become: true + + tasks: + - name: "Gather facts about the service" + ansible.builtin.service_facts: + + - name: "Check if the `rclone-yandex` service is running" + ansible.builtin.assert: + that: + - "'rclone-yandex.service' in ansible_facts.services" + - "ansible_facts.services['rclone-yandex.service'].state == 'running'" + fail_msg: "the 'rclone-yandex' service is not running!" + success_msg: "the 'rclone-yandex' service is up and running" diff --git a/roles/rclone_yandex/renovate.json b/roles/rclone_yandex/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/rclone_yandex/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/rclone_yandex/requirements.ci.txt b/roles/rclone_yandex/requirements.ci.txt new file mode 100644 index 0000000..f631d13 --- /dev/null +++ b/roles/rclone_yandex/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule~=24.9.0 +molecule-plugins[docker] +docker +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/rclone_yandex/requirements.txt b/roles/rclone_yandex/requirements.txt new file mode 100644 index 0000000..90d4055 --- /dev/null +++ b/roles/rclone_yandex/requirements.txt @@ -0,0 +1 @@ +ansible diff --git a/roles/rclone_yandex/requirements.yml b/roles/rclone_yandex/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/rclone_yandex/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/rclone_yandex/tasks/configure.yaml b/roles/rclone_yandex/tasks/configure.yaml new file mode 100644 index 0000000..c20f52c --- /dev/null +++ b/roles/rclone_yandex/tasks/configure.yaml @@ -0,0 +1,58 @@ +--- +- name: "Configure | create a group - '{{ rclone_group }}'" + ansible.builtin.group: + name: "{{ rclone_group }}" + state: present + +- name: "Configure | get GID for '{{ rclone_group }}'" + register: rclone_group_gid + changed_when: false + failed_when: not rclone_group_gid.stdout + ansible.builtin.shell: + cmd: 'set -o pipefail && getent group "{{ rclone_group }}" | cut -d: -f3' + executable: /bin/bash + +- name: "Configure | display rclone mount ownership group" + ansible.builtin.debug: + msg: "'{{ rclone_group }}' ({{ rclone_group_gid.stdout }})" + +- name: "Configure | set up directories" + loop: + - {path: "/tmp/rclone", group: "root"} + - {path: "/mnt/yandex-disk", group: "{{ rclone_group }}"} + ansible.builtin.file: + state: directory + path: "{{ item.path }}" + owner: root + group: "{{ item.group }}" + mode: "0770" + +- name: "Configure | create the [yandex] section" + ansible.builtin.lineinfile: + create: true + path: "{{ rclone_config_path }}" + line: "[yandex]" + mode: "0660" + +- name: "Configure | read in the YAMLified Yandex share credentials" + ansible.builtin.include_vars: + file: "{{ rclone_secrets }}" + +- name: "Configure | insert/update the section for the [yandex] share" + ansible.builtin.blockinfile: + path: "{{ rclone_config_path }}" + insertafter: "^[yandex]" + block: | + type = yandex + token = {{ rclone | to_json }} + +- name: "Configure | template a systemd service file" + ansible.builtin.template: + src: templates/rclone-yandex.service.j2 + dest: /lib/systemd/system/rclone-yandex.service + mode: "0660" + +# validate attribute doesn't seem to work here, unfortunately, so command to the rescue +- name: "Configure | validate the service file" + ansible.builtin.command: systemd-analyze verify rclone-yandex.service + changed_when: false diff --git a/roles/rclone_yandex/tasks/install.yaml b/roles/rclone_yandex/tasks/install.yaml new file mode 100644 index 0000000..b42ae25 --- /dev/null +++ b/roles/rclone_yandex/tasks/install.yaml @@ -0,0 +1,39 @@ +--- +- name: "Install | unzip (RHEL)" + ansible.builtin.dnf: + name: [ unzip ] + state: installed + when: ansible_os_family == 'RHEL' + +- name: "Install | unzip (Debian)" + ansible.builtin.apt: + update_cache: true + name: [ unzip ] + state: present + when: ansible_os_family == 'Debian' + +- name: "Install | rclone_yandex" + block: + - name: "Install | check if rclone binary is available in PATH" + changed_when: false + ansible.builtin.command: + cmd: "rclone --version" + rescue: + - name: "Install | fetch and install the rclone binary" # noqa: no-changed-when + register: result + failed_when: result.rc != 0 + ansible.builtin.shell: + cmd: "set -o pipefail && curl https://rclone.org/install.sh | sudo bash" + executable: /bin/bash + +- name: "Install | fuse (RHEL)" + ansible.builtin.dnf: + name: [ fuse ] + state: installed + when: ansible_os_family == 'RHEL' + +- name: "Install | fuse (Debian)" + ansible.builtin.apt: + name: [ fuse3 ] + state: present + when: ansible_os_family == 'Debian' diff --git a/roles/rclone_yandex/tasks/main.yml b/roles/rclone_yandex/tasks/main.yml new file mode 100644 index 0000000..5adbe57 --- /dev/null +++ b/roles/rclone_yandex/tasks/main.yml @@ -0,0 +1,11 @@ +# tasks file +--- + +- name: "Include installation tasks" + ansible.builtin.include_tasks: "install.yaml" + +- name: "Include configuration tasks" + ansible.builtin.include_tasks: "configure.yaml" + +- name: "Include run tasks" + ansible.builtin.include_tasks: "run.yaml" diff --git a/roles/rclone_yandex/tasks/run.yaml b/roles/rclone_yandex/tasks/run.yaml new file mode 100644 index 0000000..306b5ed --- /dev/null +++ b/roles/rclone_yandex/tasks/run.yaml @@ -0,0 +1,6 @@ +--- +- name: "Run | start the 'rclone-yandex' service" + ansible.builtin.systemd: + name: rclone-yandex + state: started + daemon_reload: true diff --git a/roles/rclone_yandex/templates/rclone-yandex.service.j2 b/roles/rclone_yandex/templates/rclone-yandex.service.j2 new file mode 100644 index 0000000..a4601d8 --- /dev/null +++ b/roles/rclone_yandex/templates/rclone-yandex.service.j2 @@ -0,0 +1,26 @@ +[Unit] +Description=rclone: Remote FUSE filesystem for cloud storage config %i +Documentation=man:rclone(1) +After=network-online.target +Wants=network-online.target + +[Service] +Type=notify +ExecStart= \ + /usr/bin/rclone mount \ + --gid={{ rclone_group_gid.stdout }} \ + --dir-perms=0770 \ + --file-perms=0660 \ + --umask=007 \ + --default-permissions \ + --allow-other \ + --config "{{ rclone_config_path }}" \ + --log-file="/tmp/rclone/yandex.log" \ + --log-level=INFO \ + --vfs-cache-mode=off \ + yandex: "{{ rclone_mountpoint }}" + +ExecStop=/bin/fusermount -u "{{ rclone_mountpoint }}" + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/roles/rclone_yandex/vars/main.yml b/roles/rclone_yandex/vars/main.yml new file mode 100644 index 0000000..d1baeea --- /dev/null +++ b/roles/rclone_yandex/vars/main.yml @@ -0,0 +1,4 @@ +--- +# vars file for rclone_yandex + +rclone_config_path: "/root/rclone.conf" From c310b109ca8e8ec11f8cb3ead36d3a7489712f9f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 14:05:56 +0300 Subject: [PATCH 68/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ecfc21b..c61b1d1 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) +- [rclone_yandex](roles/rclone_yandex/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) - [ufw](roles/ufw/README.md) - [wg_hub](roles/wg_hub/README.md) diff --git a/galaxy.yml b/galaxy.yml index 1b2fd3b..a97ca2e 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.14.0 +version: 0.15.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From b17881cf928d7adc101eaa5819bb5f8bd145c17e Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 15:59:02 +0300 Subject: [PATCH 69/96] fix ansible-lint errors --- roles/rclone_yandex/meta/main.yml | 4 ++-- roles/rclone_yandex/molecule/default/converge.yml | 2 +- roles/rclone_yandex/tasks/configure.yaml | 10 +++++----- roles/rclone_yandex/tasks/install.yaml | 12 ++++++------ .../rclone_yandex/templates/rclone-yandex.service.j2 | 4 ++-- roles/rclone_yandex/vars/main.yml | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/roles/rclone_yandex/meta/main.yml b/roles/rclone_yandex/meta/main.yml index 94fef93..2bb3f62 100644 --- a/roles/rclone_yandex/meta/main.yml +++ b/roles/rclone_yandex/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy" ] + versions: ["focal", "jammy"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/rclone_yandex/molecule/default/converge.yml b/roles/rclone_yandex/molecule/default/converge.yml index afa49dd..7e6452e 100644 --- a/roles/rclone_yandex/molecule/default/converge.yml +++ b/roles/rclone_yandex/molecule/default/converge.yml @@ -13,7 +13,7 @@ autoclean: true roles: - - role: genlab.rclone_yandex + - role: genlab.common.rclone_yandex rclone_mountpoint: /mnt/yandex-disk rclone_group: yandex rclone_secrets: secrets/token.yml diff --git a/roles/rclone_yandex/tasks/configure.yaml b/roles/rclone_yandex/tasks/configure.yaml index c20f52c..60715f7 100644 --- a/roles/rclone_yandex/tasks/configure.yaml +++ b/roles/rclone_yandex/tasks/configure.yaml @@ -5,16 +5,16 @@ state: present - name: "Configure | get GID for '{{ rclone_group }}'" - register: rclone_group_gid + register: rclone_yandex_group_gid changed_when: false - failed_when: not rclone_group_gid.stdout + failed_when: not rclone_yandex_group_gid.stdout ansible.builtin.shell: cmd: 'set -o pipefail && getent group "{{ rclone_group }}" | cut -d: -f3' executable: /bin/bash - name: "Configure | display rclone mount ownership group" ansible.builtin.debug: - msg: "'{{ rclone_group }}' ({{ rclone_group_gid.stdout }})" + msg: "'{{ rclone_group }}' ({{ rclone_yandex_group_gid.stdout }})" - name: "Configure | set up directories" loop: @@ -30,7 +30,7 @@ - name: "Configure | create the [yandex] section" ansible.builtin.lineinfile: create: true - path: "{{ rclone_config_path }}" + path: "{{ rclone_yandex_config_path }}" line: "[yandex]" mode: "0660" @@ -40,7 +40,7 @@ - name: "Configure | insert/update the section for the [yandex] share" ansible.builtin.blockinfile: - path: "{{ rclone_config_path }}" + path: "{{ rclone_yandex_config_path }}" insertafter: "^[yandex]" block: | type = yandex diff --git a/roles/rclone_yandex/tasks/install.yaml b/roles/rclone_yandex/tasks/install.yaml index b42ae25..71cba8b 100644 --- a/roles/rclone_yandex/tasks/install.yaml +++ b/roles/rclone_yandex/tasks/install.yaml @@ -1,14 +1,14 @@ --- - name: "Install | unzip (RHEL)" ansible.builtin.dnf: - name: [ unzip ] + name: [unzip] state: installed when: ansible_os_family == 'RHEL' - name: "Install | unzip (Debian)" ansible.builtin.apt: update_cache: true - name: [ unzip ] + name: [unzip] state: present when: ansible_os_family == 'Debian' @@ -20,20 +20,20 @@ cmd: "rclone --version" rescue: - name: "Install | fetch and install the rclone binary" # noqa: no-changed-when - register: result - failed_when: result.rc != 0 + register: rclone_yandex_result + failed_when: rclone_yandex_result.rc != 0 ansible.builtin.shell: cmd: "set -o pipefail && curl https://rclone.org/install.sh | sudo bash" executable: /bin/bash - name: "Install | fuse (RHEL)" ansible.builtin.dnf: - name: [ fuse ] + name: [fuse] state: installed when: ansible_os_family == 'RHEL' - name: "Install | fuse (Debian)" ansible.builtin.apt: - name: [ fuse3 ] + name: [fuse3] state: present when: ansible_os_family == 'Debian' diff --git a/roles/rclone_yandex/templates/rclone-yandex.service.j2 b/roles/rclone_yandex/templates/rclone-yandex.service.j2 index a4601d8..c8df724 100644 --- a/roles/rclone_yandex/templates/rclone-yandex.service.j2 +++ b/roles/rclone_yandex/templates/rclone-yandex.service.j2 @@ -8,13 +8,13 @@ Wants=network-online.target Type=notify ExecStart= \ /usr/bin/rclone mount \ - --gid={{ rclone_group_gid.stdout }} \ + --gid={{ rclone_yandex_group_gid.stdout }} \ --dir-perms=0770 \ --file-perms=0660 \ --umask=007 \ --default-permissions \ --allow-other \ - --config "{{ rclone_config_path }}" \ + --config "{{ rclone_yandex_config_path }}" \ --log-file="/tmp/rclone/yandex.log" \ --log-level=INFO \ --vfs-cache-mode=off \ diff --git a/roles/rclone_yandex/vars/main.yml b/roles/rclone_yandex/vars/main.yml index d1baeea..ce372de 100644 --- a/roles/rclone_yandex/vars/main.yml +++ b/roles/rclone_yandex/vars/main.yml @@ -1,4 +1,4 @@ --- # vars file for rclone_yandex -rclone_config_path: "/root/rclone.conf" +rclone_yandex_config_path: "/root/rclone.conf" From 475c95b00511bb8bdecc46f5af0947e94144a9b4 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:00:29 +0300 Subject: [PATCH 70/96] remove garbage --- roles/rclone_yandex/.ansible-lint | 16 ---------------- roles/rclone_yandex/.yamllint | 8 -------- roles/rclone_yandex/environment.ci.yml | 11 ----------- roles/rclone_yandex/environment.yml | 9 --------- roles/rclone_yandex/renovate.json | 6 ------ roles/rclone_yandex/requirements.ci.txt | 6 ------ roles/rclone_yandex/requirements.txt | 1 - roles/rclone_yandex/requirements.yml | 3 --- 8 files changed, 60 deletions(-) delete mode 100644 roles/rclone_yandex/.ansible-lint delete mode 100644 roles/rclone_yandex/.yamllint delete mode 100644 roles/rclone_yandex/environment.ci.yml delete mode 100644 roles/rclone_yandex/environment.yml delete mode 100644 roles/rclone_yandex/renovate.json delete mode 100644 roles/rclone_yandex/requirements.ci.txt delete mode 100644 roles/rclone_yandex/requirements.txt delete mode 100644 roles/rclone_yandex/requirements.yml diff --git a/roles/rclone_yandex/.ansible-lint b/roles/rclone_yandex/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/rclone_yandex/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/rclone_yandex/.yamllint b/roles/rclone_yandex/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/rclone_yandex/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/rclone_yandex/environment.ci.yml b/roles/rclone_yandex/environment.ci.yml deleted file mode 100644 index 1c6d7b1..0000000 --- a/roles/rclone_yandex/environment.ci.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-rclone-yandex -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=22.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/rclone_yandex/environment.yml b/roles/rclone_yandex/environment.yml deleted file mode 100644 index b86b81f..0000000 --- a/roles/rclone_yandex/environment.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-rclone-yandex -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=22.2 - - pip: - - -r requirements.txt diff --git a/roles/rclone_yandex/renovate.json b/roles/rclone_yandex/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/rclone_yandex/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/rclone_yandex/requirements.ci.txt b/roles/rclone_yandex/requirements.ci.txt deleted file mode 100644 index f631d13..0000000 --- a/roles/rclone_yandex/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule~=24.9.0 -molecule-plugins[docker] -docker -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/rclone_yandex/requirements.txt b/roles/rclone_yandex/requirements.txt deleted file mode 100644 index 90d4055..0000000 --- a/roles/rclone_yandex/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible diff --git a/roles/rclone_yandex/requirements.yml b/roles/rclone_yandex/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/rclone_yandex/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From 236bd1ff72759170e7de3b54e06c42bb97f50013 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:16:43 +0300 Subject: [PATCH 71/96] add loki role and fix linter errors --- roles/loki/.ansible-lint | 16 ++++ roles/loki/.gitignore | 2 + roles/loki/.yamllint | 8 ++ roles/loki/README.md | 43 ++++++++++ roles/loki/defaults/main.yml | 4 + roles/loki/environment.ci.yml | 10 +++ roles/loki/environment.yml | 9 ++ roles/loki/handlers/main.yml | 10 +++ roles/loki/meta/main.yml | 17 ++++ roles/loki/molecule/default/converge.yml | 5 ++ roles/loki/molecule/default/molecule.yml | 22 +++++ roles/loki/molecule/default/verify.yml | 33 ++++++++ roles/loki/renovate.json | 6 ++ roles/loki/requirements.ci.txt | 6 ++ roles/loki/requirements.txt | 1 + roles/loki/tasks/main.yml | 101 +++++++++++++++++++++++ roles/loki/templates/loki.service.j2 | 11 +++ roles/loki/templates/loki.yml.j2 | 83 +++++++++++++++++++ roles/loki/templates/loki_test.yml.j2 | 45 ++++++++++ roles/loki/vars/main.yml | 1 + 20 files changed, 433 insertions(+) create mode 100644 roles/loki/.ansible-lint create mode 100644 roles/loki/.gitignore create mode 100644 roles/loki/.yamllint create mode 100644 roles/loki/README.md create mode 100644 roles/loki/defaults/main.yml create mode 100644 roles/loki/environment.ci.yml create mode 100644 roles/loki/environment.yml create mode 100644 roles/loki/handlers/main.yml create mode 100644 roles/loki/meta/main.yml create mode 100644 roles/loki/molecule/default/converge.yml create mode 100644 roles/loki/molecule/default/molecule.yml create mode 100644 roles/loki/molecule/default/verify.yml create mode 100644 roles/loki/renovate.json create mode 100644 roles/loki/requirements.ci.txt create mode 100644 roles/loki/requirements.txt create mode 100644 roles/loki/tasks/main.yml create mode 100644 roles/loki/templates/loki.service.j2 create mode 100644 roles/loki/templates/loki.yml.j2 create mode 100644 roles/loki/templates/loki_test.yml.j2 create mode 100644 roles/loki/vars/main.yml diff --git a/roles/loki/.ansible-lint b/roles/loki/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/loki/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/loki/.gitignore b/roles/loki/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/loki/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/loki/.yamllint b/roles/loki/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/loki/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/loki/README.md b/roles/loki/README.md new file mode 100644 index 0000000..8a05ac1 --- /dev/null +++ b/roles/loki/README.md @@ -0,0 +1,43 @@ +loki +========= + +Installs Loki as a `systemd` service. + +Requirements +------------ + +Role Variables +-------------- + +```yaml +loki_version: Loki version to be deployed +loki_storage_path: where to put Loki's files, including log data and positions (default: /data/loki) +loki_port: Loki will listen on this port (default: 3000) +``` + +Dependencies +------------ + +No + +Example Playbook +---------------- + +```yaml +roles: + - role: loki + loki_version: 2.7.3 +``` + + +License +------- + +MIT + +Author Information +------------------ + +Alexander Gorelyshev (corvus-migratorius@proton.me) and Danila Danilkin + +Genlab LLC diff --git a/roles/loki/defaults/main.yml b/roles/loki/defaults/main.yml new file mode 100644 index 0000000..08a7293 --- /dev/null +++ b/roles/loki/defaults/main.yml @@ -0,0 +1,4 @@ +--- +loki_version: 3.4.2 +loki_storage_path: /data/loki +loki_port: 3100 diff --git a/roles/loki/environment.ci.yml b/roles/loki/environment.ci.yml new file mode 100644 index 0000000..d96d2e1 --- /dev/null +++ b/roles/loki/environment.ci.yml @@ -0,0 +1,10 @@ +--- +name: ansible-loki +channels: + - conda-forge +dependencies: + - python~=3.12 + - pip>=22.2 + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/loki/environment.yml b/roles/loki/environment.yml new file mode 100644 index 0000000..bdd0220 --- /dev/null +++ b/roles/loki/environment.yml @@ -0,0 +1,9 @@ +--- +name: ansible-loki +channels: + - conda-forge +dependencies: + - python~=3.12 + - pip>=22.2 + - pip: + - -r requirements.txt diff --git a/roles/loki/handlers/main.yml b/roles/loki/handlers/main.yml new file mode 100644 index 0000000..e6d7f41 --- /dev/null +++ b/roles/loki/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: "Restart the Loki daemon" + ansible.builtin.systemd: + name: loki + state: restarted + + +- name: "Reload the Loki daemon configuration" + ansible.builtin.systemd: + daemon_reload: true diff --git a/roles/loki/meta/main.yml b/roles/loki/meta/main.yml new file mode 100644 index 0000000..2ca0728 --- /dev/null +++ b/roles/loki/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: loki + namespace: genlab + author: "Danila Danilkin" + company: "Genlab, LLC" + description: "Deploy Loki as a systemd service" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/loki/molecule/default/converge.yml b/roles/loki/molecule/default/converge.yml new file mode 100644 index 0000000..b95fe26 --- /dev/null +++ b/roles/loki/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.loki diff --git a/roles/loki/molecule/default/molecule.yml b/roles/loki/molecule/default/molecule.yml new file mode 100644 index 0000000..de54042 --- /dev/null +++ b/roles/loki/molecule/default/molecule.yml @@ -0,0 +1,22 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: ubuntu + image: geerlingguy/docker-ubuntu2204-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true +provisioner: + name: ansible +verifier: + name: ansible +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/loki/molecule/default/verify.yml b/roles/loki/molecule/default/verify.yml new file mode 100644 index 0000000..62dd143 --- /dev/null +++ b/roles/loki/molecule/default/verify.yml @@ -0,0 +1,33 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + tasks: + # https://github.com/ansible/molecule/issues/3587#issuecomment-1158650179 + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: ['yml'] + + - name: "Sanity check the Loki daemon" + retries: 5 + delay: 1 + register: loki_this + failed_when: "loki_version not in loki_this.content" + ansible.builtin.uri: + url: "http://localhost:{{ loki_port }}/metrics" + return_content: true + + - name: "Ensure that locgli is installed and able to run" + register: loki_logcli + failed_when: loki_logcli.rc != 0 + changed_when: false + ansible.builtin.command: + cmd: logcli --version + + - name: "Check logcli version" + ansible.builtin.assert: + that: "'version {{ loki_version }}' in loki_logcli.stderr" + success_msg: "logcli version {{ loki_version }} is installed and working" + fail_msg: "logcli version {{ loki_version }} is not installed or not working correctly" diff --git a/roles/loki/renovate.json b/roles/loki/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/loki/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/loki/requirements.ci.txt b/roles/loki/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/loki/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/loki/requirements.txt b/roles/loki/requirements.txt new file mode 100644 index 0000000..92f8677 --- /dev/null +++ b/roles/loki/requirements.txt @@ -0,0 +1 @@ +ansible~=11.3.0 diff --git a/roles/loki/tasks/main.yml b/roles/loki/tasks/main.yml new file mode 100644 index 0000000..266744e --- /dev/null +++ b/roles/loki/tasks/main.yml @@ -0,0 +1,101 @@ +--- +- name: "Install unzip package" + ansible.builtin.apt: + name: unzip + state: present + cache_valid_time: 3600 + + +- name: "Create Loki directories" + with_items: + - {path: '/opt/loki', mode: '0755'} + - {path: '/etc/loki', mode: '0644'} + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + owner: root + group: root + mode: "{{ item.mode }}" + + +- name: "Template Loki config file" + notify: "Restart the Loki daemon" + ansible.builtin.template: + src: "loki.yml.j2" + dest: "/etc/loki/loki.yml" + owner: root + group: root + mode: "0644" + + +- name: "Install requested Loki version" + block: + - name: "Check Loki version" + ansible.builtin.command: /opt/loki/loki -version + register: loki_version_check + changed_when: false + + - name: "Assert version correctness" + notify: "Restart the Loki daemon" + ansible.builtin.assert: + that: "loki_version in loki_version_check.stdout" + success_msg: "Expected Loki version available ({{ loki_version }})" + fail_msg: "Expected version '{{ loki_version }}'; available is '{{ loki_version_check.stdout }}'" + + rescue: + - name: "Install Loki if it is not present ({{ loki_version }})" + ansible.builtin.unarchive: + src: "https://github.com/grafana/loki/releases/download/v\ + {{ loki_version }}/loki-linux-amd64.zip" + dest: /opt/loki/ + remote_src: true + + - name: "Rename and set permissions for the Loki binary" + ansible.builtin.copy: + src: "/opt/loki/loki-linux-amd64" + dest: /opt/loki/loki + owner: root + group: root + mode: "0755" + remote_src: true + + - name: "Cleanup downloaded file" + ansible.builtin.file: + path: /opt/loki/loki-linux-amd64 + state: absent + + - name: "Install logcli" + ansible.builtin.unarchive: + src: "https://github.com/grafana/loki/releases/download/v\ + {{ loki_version }}/logcli-linux-amd64.zip" + dest: /usr/local/bin + remote_src: true + + - name: "Rename and set permissions for the Logcli binary" + ansible.builtin.copy: + src: "/usr/local/bin/logcli-linux-amd64" + dest: "/usr/local/bin/logcli" + owner: root + group: root + mode: "0755" + remote_src: true + + - name: "Cleanup downloaded file" + ansible.builtin.file: + path: /usr/local/bin/logcli-linux-amd64 + state: absent + +- name: "Template the systemd unit file" + notify: "Reload the Loki daemon configuration" + ansible.builtin.template: + src: templates/loki.service.j2 + dest: /etc/systemd/system/loki.service + owner: root + group: root + mode: "0755" + +- name: "Start and enable the Loki daemon" + ansible.builtin.systemd: + name: loki + state: started + enabled: true diff --git a/roles/loki/templates/loki.service.j2 b/roles/loki/templates/loki.service.j2 new file mode 100644 index 0000000..6cc23bc --- /dev/null +++ b/roles/loki/templates/loki.service.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Loki service +After=network.target + +[Service] +Type=simple +User=root +ExecStart=/opt/loki/loki -config.file /etc/loki/loki.yml + +[Install] +WantedBy=multi-user.target diff --git a/roles/loki/templates/loki.yml.j2 b/roles/loki/templates/loki.yml.j2 new file mode 100644 index 0000000..8236f15 --- /dev/null +++ b/roles/loki/templates/loki.yml.j2 @@ -0,0 +1,83 @@ +auth_enabled: false + +server: + http_listen_port: {{ loki_port }} + # grpc_listen_port: 9096 + # log_level: debug + # grpc_server_max_concurrent_streams: 1000 + +common: + instance_addr: 127.0.0.1 + path_prefix: {{ loki_storage_path }} + storage: + filesystem: + chunks_directory: {{ loki_storage_path }}/chunks + rules_directory: {{ loki_storage_path }}/rules + replication_factor: 1 + ring: + kvstore: + store: inmemory + +# ingester: +# lifecycler: +# address: 127.0.0.1 +# ring: +# kvstore: +# store: inmemory +# replication_factor: 1 +# final_sleep: 0s +# chunk_idle_period: 5m +# chunk_retain_period: 30s +# max_transfer_retries: 0 + +schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +pattern_ingester: + enabled: true + metric_aggregation: + loki_address: localhost:3100 + +# storage_config: +# boltdb: +# directory: {{ loki_storage_path }}/index + +# filesystem: +# directory: {{ loki_storage_path }}/chunks + +# ruler: +# alertmanager_url: http://localhost:9093 + +query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +frontend: + encoding: protobuf + +# querier: +# engine: +# enable_multi_variant_queries: true + +limits_config: + metric_aggregation_enabled: true + +# chunk_store_config: +# max_look_back_period: 0s + +# table_manager: +# retention_deletes_enabled: false +# retention_period: 0s + +analytics: + reporting_enabled: false diff --git a/roles/loki/templates/loki_test.yml.j2 b/roles/loki/templates/loki_test.yml.j2 new file mode 100644 index 0000000..954f214 --- /dev/null +++ b/roles/loki/templates/loki_test.yml.j2 @@ -0,0 +1,45 @@ +auth_enabled: false + +server: + http_listen_port: 3200 + +ingester: + lifecycler: + address: 127.0.0.1 + ring: + kvstore: + store: inmemory + replication_factor: 1 + final_sleep: 0s + chunk_idle_period: 5m + chunk_retain_period: 30s + max_transfer_retries: 0 + +schema_config: + configs: + - from: 2018-04-15 + store: boltdb + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 168h + +storage_config: + boltdb: + directory: /data/loki/index + + filesystem: + directory: /data/loki/chunks + +limits_config: + enforce_metric_name: false + reject_old_samples: true + reject_old_samples_max_age: 168h + +chunk_store_config: + max_look_back_period: 0s + +table_manager: + retention_deletes_enabled: false + retention_period: 0s diff --git a/roles/loki/vars/main.yml b/roles/loki/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/loki/vars/main.yml @@ -0,0 +1 @@ +--- From 13548f4ea8610922192e34725a06199a1f1ff1e7 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:18:12 +0300 Subject: [PATCH 72/96] remove garbage --- roles/loki/.ansible-lint | 16 ---------------- roles/loki/.gitignore | 2 -- roles/loki/.yamllint | 8 -------- roles/loki/environment.ci.yml | 10 ---------- roles/loki/environment.yml | 9 --------- roles/loki/renovate.json | 6 ------ roles/loki/requirements.ci.txt | 6 ------ roles/loki/requirements.txt | 1 - 8 files changed, 58 deletions(-) delete mode 100644 roles/loki/.ansible-lint delete mode 100644 roles/loki/.gitignore delete mode 100644 roles/loki/.yamllint delete mode 100644 roles/loki/environment.ci.yml delete mode 100644 roles/loki/environment.yml delete mode 100644 roles/loki/renovate.json delete mode 100644 roles/loki/requirements.ci.txt delete mode 100644 roles/loki/requirements.txt diff --git a/roles/loki/.ansible-lint b/roles/loki/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/loki/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/loki/.gitignore b/roles/loki/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/loki/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/loki/.yamllint b/roles/loki/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/loki/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/loki/environment.ci.yml b/roles/loki/environment.ci.yml deleted file mode 100644 index d96d2e1..0000000 --- a/roles/loki/environment.ci.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: ansible-loki -channels: - - conda-forge -dependencies: - - python~=3.12 - - pip>=22.2 - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/loki/environment.yml b/roles/loki/environment.yml deleted file mode 100644 index bdd0220..0000000 --- a/roles/loki/environment.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-loki -channels: - - conda-forge -dependencies: - - python~=3.12 - - pip>=22.2 - - pip: - - -r requirements.txt diff --git a/roles/loki/renovate.json b/roles/loki/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/loki/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/loki/requirements.ci.txt b/roles/loki/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/loki/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/loki/requirements.txt b/roles/loki/requirements.txt deleted file mode 100644 index 92f8677..0000000 --- a/roles/loki/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.3.0 From adeda4e8a0935644650a614956c7f690a9c42565 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:18:59 +0300 Subject: [PATCH 73/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c61b1d1..941083e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - [grafana](roles/grafana/README.md) - [ipmi_exporter](roles/ipmi_exporter/README.md) - [karma](roles/karma/README.md) +- [loki](roles/loki/README.md) - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) diff --git a/galaxy.yml b/galaxy.yml index a97ca2e..1b66324 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.15.0 +version: 0.16.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 4b44dbc6083455cebbb97ce7e9545c24ce1837df Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:28:02 +0300 Subject: [PATCH 74/96] fic kics error --- roles/loki/molecule/default/verify.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/roles/loki/molecule/default/verify.yml b/roles/loki/molecule/default/verify.yml index 62dd143..57043c6 100644 --- a/roles/loki/molecule/default/verify.yml +++ b/roles/loki/molecule/default/verify.yml @@ -10,6 +10,8 @@ dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" extensions: ['yml'] + # kics-scan ignore + # kics wants localhost on https but i dont - name: "Sanity check the Loki daemon" retries: 5 delay: 1 From 3c03a4ba8997d62cb4976a5f494a52fd0f182292 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:32:01 +0300 Subject: [PATCH 75/96] fix kics errors --- roles/loki/molecule/default/verify.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/roles/loki/molecule/default/verify.yml b/roles/loki/molecule/default/verify.yml index 57043c6..9d764c5 100644 --- a/roles/loki/molecule/default/verify.yml +++ b/roles/loki/molecule/default/verify.yml @@ -10,14 +10,13 @@ dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" extensions: ['yml'] - # kics-scan ignore - # kics wants localhost on https but i dont - name: "Sanity check the Loki daemon" retries: 5 delay: 1 register: loki_this failed_when: "loki_version not in loki_this.content" ansible.builtin.uri: + # kics-scan ignore - kics wants https on localhost but i dont url: "http://localhost:{{ loki_port }}/metrics" return_content: true From 283ac17bff14987de07f071285c1dfbcafb663fe Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:34:42 +0300 Subject: [PATCH 76/96] fix kics errors --- roles/loki/molecule/default/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/loki/molecule/default/verify.yml b/roles/loki/molecule/default/verify.yml index 9d764c5..de1933c 100644 --- a/roles/loki/molecule/default/verify.yml +++ b/roles/loki/molecule/default/verify.yml @@ -16,7 +16,7 @@ register: loki_this failed_when: "loki_version not in loki_this.content" ansible.builtin.uri: - # kics-scan ignore - kics wants https on localhost but i dont + # # kics-scan ignore-line - kics wants https on localhost but i dont url: "http://localhost:{{ loki_port }}/metrics" return_content: true From da743a733bd944eb36b2707af7a7fa18eaf8d658 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 16:36:32 +0300 Subject: [PATCH 77/96] fix kics errors --- roles/loki/molecule/default/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/loki/molecule/default/verify.yml b/roles/loki/molecule/default/verify.yml index de1933c..c18de88 100644 --- a/roles/loki/molecule/default/verify.yml +++ b/roles/loki/molecule/default/verify.yml @@ -16,7 +16,7 @@ register: loki_this failed_when: "loki_version not in loki_this.content" ansible.builtin.uri: - # # kics-scan ignore-line - kics wants https on localhost but i dont + # kics-scan ignore-line - kics wants https on localhost but i dont url: "http://localhost:{{ loki_port }}/metrics" return_content: true From fbd36ee3fcca9a15e692fdb011b57e9aa5e8bdaf Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 18:03:23 +0300 Subject: [PATCH 78/96] add sftp_share role --- roles/sftp_share/.ansible-lint | 16 ++++ roles/sftp_share/.gitignore | 3 + roles/sftp_share/.yamllint | 8 ++ roles/sftp_share/README.md | 73 +++++++++++++++ roles/sftp_share/conda.dev.yml | 11 +++ roles/sftp_share/conda.prod.yml | 9 ++ roles/sftp_share/defaults/main.yml | 2 + roles/sftp_share/handlers/main.yml | 7 ++ roles/sftp_share/meta/main.yml | 17 ++++ .../sftp_share/molecule/default/converge.yml | 39 ++++++++ .../sftp_share/molecule/default/molecule.yml | 27 ++++++ .../molecule/default/ssh/id_ed25519 | 7 ++ .../molecule/default/ssh/id_ed25519.pub | 1 + roles/sftp_share/molecule/default/verify.yml | 91 +++++++++++++++++++ roles/sftp_share/renovate.json | 6 ++ roles/sftp_share/requirements.ci.txt | 6 ++ roles/sftp_share/requirements.txt | 1 + roles/sftp_share/requirements.yml | 3 + roles/sftp_share/tasks/main.yml | 38 ++++++++ .../sftp_share/templates/sshd_config.conf.j2 | 5 + roles/sftp_share/vars/main.yml | 1 + 21 files changed, 371 insertions(+) create mode 100644 roles/sftp_share/.ansible-lint create mode 100644 roles/sftp_share/.gitignore create mode 100644 roles/sftp_share/.yamllint create mode 100644 roles/sftp_share/README.md create mode 100644 roles/sftp_share/conda.dev.yml create mode 100644 roles/sftp_share/conda.prod.yml create mode 100644 roles/sftp_share/defaults/main.yml create mode 100644 roles/sftp_share/handlers/main.yml create mode 100644 roles/sftp_share/meta/main.yml create mode 100644 roles/sftp_share/molecule/default/converge.yml create mode 100644 roles/sftp_share/molecule/default/molecule.yml create mode 100644 roles/sftp_share/molecule/default/ssh/id_ed25519 create mode 100644 roles/sftp_share/molecule/default/ssh/id_ed25519.pub create mode 100644 roles/sftp_share/molecule/default/verify.yml create mode 100644 roles/sftp_share/renovate.json create mode 100644 roles/sftp_share/requirements.ci.txt create mode 100644 roles/sftp_share/requirements.txt create mode 100644 roles/sftp_share/requirements.yml create mode 100644 roles/sftp_share/tasks/main.yml create mode 100644 roles/sftp_share/templates/sshd_config.conf.j2 create mode 100644 roles/sftp_share/vars/main.yml diff --git a/roles/sftp_share/.ansible-lint b/roles/sftp_share/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/sftp_share/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/sftp_share/.gitignore b/roles/sftp_share/.gitignore new file mode 100644 index 0000000..292f339 --- /dev/null +++ b/roles/sftp_share/.gitignore @@ -0,0 +1,3 @@ +.vscode +.idea +.ansible \ No newline at end of file diff --git a/roles/sftp_share/.yamllint b/roles/sftp_share/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/sftp_share/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/sftp_share/README.md b/roles/sftp_share/README.md new file mode 100644 index 0000000..2a9c476 --- /dev/null +++ b/roles/sftp_share/README.md @@ -0,0 +1,73 @@ +SFTP Share Ansible Role +========= + +Creates a user account and directories for secured SFTP data exchange (one user = one share). + +The role creates a dedicated SSH config under `/etc/ssh/sshd_config.d/`, configuring: + +- chroot into the directory of the share +- limits commands to `internal-sftp` +- further controls SFTP permissions for the share + +Disk quota management is not included. + +Requirements +------------ + +None + +Role Variables +-------------- + +`sftp_username` — user account name on the sFTP server +`sftp_pubkey` — the public part of the SSH key the user will be using to connect to the server +`sftp_root` — directory to put the SFTP shares under +`sftp_transfers_groupname` — group owning `{{sftp_root}}/{{sftp_username}}-uploads/transfers` +`sftp_permissions` — list of permissions, see below for supported (default: `open,close,read,write,lstat,fstat,opendir,readdir,remove,mkdir,rmdir,realpath,rename`) + +List Of SFTP Operations +------------------------ +`open` — open a file for reading or writing +`close` — close an opened file +`read` — read data from a file +`write` — write data to a file +`lstat` — get file attributes without following symlinks +`fstat` — get attributes of an opened file +`setstat` — change file attributes (chmod, chown, etc.) +`fsetstat` — change attributes of an opened file +`opendir` — open a directory for reading +`readdir` — read the contents of a directory +`remove` — delete a file +`mkdir` — create a new directory +`rmdir` — remove an empty directory +`realpath` — resolve absolute path of a file or directory (inside chroot) +`stat` — get file attributes (follows symlinks) +`rename` — rename a file or directory +`readlink` — read the target of a symbolic link +`symlink` — create a symbolic link + +For example: + +- Default: `open,close,read,write,lstat,fstat,opendir,readdir,remove,mkdir,rmdir,realpath,rename` +- Read only: `open,close,read,lstat,fstat,opendir,readdir,realpath,stat` +- Required to establish an SFTP connection: `realpath` + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See `molecule/default/converge.yml` for an example role invocation. + +License +------- + +BSD + +Author Information +------------------ + +malyuk.ss@genlab.llc diff --git a/roles/sftp_share/conda.dev.yml b/roles/sftp_share/conda.dev.yml new file mode 100644 index 0000000..421257c --- /dev/null +++ b/roles/sftp_share/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-sftp-share +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/sftp_share/conda.prod.yml b/roles/sftp_share/conda.prod.yml new file mode 100644 index 0000000..2f50d61 --- /dev/null +++ b/roles/sftp_share/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-sftp-share +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/sftp_share/defaults/main.yml b/roles/sftp_share/defaults/main.yml new file mode 100644 index 0000000..d742061 --- /dev/null +++ b/roles/sftp_share/defaults/main.yml @@ -0,0 +1,2 @@ +--- +sftp_permissions: "open,close,read,write,lstat,fstat,opendir,readdir,remove,mkdir,rmdir,realpath,rename" diff --git a/roles/sftp_share/handlers/main.yml b/roles/sftp_share/handlers/main.yml new file mode 100644 index 0000000..46760ca --- /dev/null +++ b/roles/sftp_share/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Restart sshd + ansible.builtin.systemd_service: + name: ssh + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/sftp_share/meta/main.yml b/roles/sftp_share/meta/main.yml new file mode 100644 index 0000000..06a754f --- /dev/null +++ b/roles/sftp_share/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "sftp_share" + namespace: genlab + author: "Sergey Malyuk" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/sftp_share/molecule/default/converge.yml b/roles/sftp_share/molecule/default/converge.yml new file mode 100644 index 0000000..a6cba4e --- /dev/null +++ b/roles/sftp_share/molecule/default/converge.yml @@ -0,0 +1,39 @@ +--- +- name: Converge + hosts: all + + pre_tasks: + - name: Create test group + ansible.builtin.group: + name: testgrp + state: present + + - name: Ensure OpenSSH server is installed + ansible.builtin.apt: + name: openssh-server + state: present + update_cache: true + + - name: Ensure the `/root/.ssh` directory exists + ansible.builtin.file: + state: directory + path: "/root/.ssh" + mode: "0700" + owner: root + group: root + + - name: Provide an SSH private key for testing purposes + ansible.builtin.copy: + src: "ssh/id_ed25519" + dest: "/root/.ssh/id_ed25519" + mode: "0600" + owner: root + group: root + + roles: + - role: genlab.sftp_share + sftp_username: "testusr" + sftp_pubkey: "{{ lookup('file', 'ssh/id_ed25519.pub') }}" + sftp_root: "/primary/data" + sftp_transfers_groupname: "testgrp" + sftp_permissions: "open,close,read,lstat,fstat,opendir,readdir,realpath,stat" # read-only diff --git a/roles/sftp_share/molecule/default/molecule.yml b/roles/sftp_share/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/sftp_share/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/sftp_share/molecule/default/ssh/id_ed25519 b/roles/sftp_share/molecule/default/ssh/id_ed25519 new file mode 100644 index 0000000..f18502b --- /dev/null +++ b/roles/sftp_share/molecule/default/ssh/id_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBSSKh3Oc0GgnvSzCmvloaoBVpa3/ZcEUhimzih81XMUAAAAJCpucRAqbnE +QAAAAAtzc2gtZWQyNTUxOQAAACBSSKh3Oc0GgnvSzCmvloaoBVpa3/ZcEUhimzih81XMUA +AAAEDKy8xJ6QdOj+DG1oromiQ0TmwnTWFcMwjUMdqIZFd8blJIqHc5zQaCe9LMKa+WhqgF +Wlrf9lwRSGKbOKHzVcxQAAAAC3Jvb3RAdWJ1bnR1AQI= +-----END OPENSSH PRIVATE KEY----- diff --git a/roles/sftp_share/molecule/default/ssh/id_ed25519.pub b/roles/sftp_share/molecule/default/ssh/id_ed25519.pub new file mode 100644 index 0000000..6c1eb0d --- /dev/null +++ b/roles/sftp_share/molecule/default/ssh/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFJIqHc5zQaCe9LMKa+WhqgFWlrf9lwRSGKbOKHzVcxQ root@ubuntu diff --git a/roles/sftp_share/molecule/default/verify.yml b/roles/sftp_share/molecule/default/verify.yml new file mode 100644 index 0000000..43a853e --- /dev/null +++ b/roles/sftp_share/molecule/default/verify.yml @@ -0,0 +1,91 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + sftp_username: "testusr" + sftp_pubkey: "{{ lookup('file', 'ssh/id_ed25519.pub') }}" + sftp_root: "/primary/data" + sftp_transfers_groupname: "testgrp" + + tasks: + - name: Gather user SFTP directory info + ansible.builtin.stat: + path: "{{ sftp_root }}/{{ sftp_username }}-uploads" + register: sftp_root_stat + + - name: Assert that user SFTP directory has correct owner + ansible.builtin.assert: + that: + - sftp_root_stat.stat.pw_name == "root" + - sftp_root_stat.stat.gr_name == "root" + success_msg: "SFTP directory {{ sftp_root }}/{{ sftp_username }}-uploads has correct owner root:root" + fail_msg: | + "SFTP directory {{ sftp_root }}/{{ sftp_username }}-uploads does not have correct owner root:root, + actual owner is {{ sftp_root_stat.stat.pw_name }}:{{ sftp_root_stat.stat.gr_name }}" + + - name: Gather transfers directory info + ansible.builtin.stat: + path: "{{ sftp_root }}/{{ sftp_username }}-uploads/transfers" + register: transfers_stat + + - name: Assert that transfers directory has correct owner + ansible.builtin.assert: + that: + - transfers_stat.stat.pw_name == sftp_username + - transfers_stat.stat.gr_name == sftp_transfers_groupname + success_msg: "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers has correct owner {{ sftp_username }}:{{ sftp_transfers_groupname }}" + fail_msg: | + "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers does not have correct owner {{ sftp_username }}:{{ sftp_transfers_groupname }}, + actual owner is {{ transfers_stat.stat.pw_name }}:{{ transfers_stat.stat.gr_name }}" + + - name: Assert that transfers directory has correct permissions + ansible.builtin.assert: + that: + - transfers_stat.stat.mode == "2770" + success_msg: "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers has correct permissions 2770" + fail_msg: | + "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers does not have correct permissions 2770, + actual permissions are {{ sftp_root_stat.stat.mode }}" + + - name: Get user info + ansible.builtin.getent: + database: passwd + key: "{{ sftp_username }}" + register: user_info + + - name: Check user exists + ansible.builtin.assert: + that: + - user_info is defined + success_msg: "User {{ sftp_username }} exists" + fail_msg: "User {{ sftp_username }} does not exist" + + - name: Check that sshd refuses non-SFTP connection attempts + changed_when: false + failed_when: ssh.stdout != expected + register: ssh + vars: + expected: "This service allows sftp connections only." + ansible.builtin.command: "ssh -l {{ sftp_username }} 127.0.0.1 -o StrictHostKeyChecking=no exit" + + - name: Check that sFTP chroots into /transfers + changed_when: false + register: sftp + failed_when: sftp.stdout != expected + vars: + expected: "sftp> pwd\nRemote working directory: /transfers" + ansible.builtin.shell: + executable: /bin/bash + cmd: 'set -o pipefail; echo "pwd" | sftp -b - -o StrictHostKeyChecking=no {{ sftp_username | quote }}@127.0.0.1' + + - name: Check that non-whitelisted sFTP permissions are denied + changed_when: false + register: sftp_mkdir + failed_when: sftp_mkdir.stderr != expected + vars: + expected: 'remote mkdir "/transfers/foo": Permission denied' + ansible.builtin.shell: + executable: /bin/bash + cmd: 'set -o pipefail; echo "mkdir foo" | sftp -b - -o StrictHostKeyChecking=no {{ sftp_username | quote }}@127.0.0.1' diff --git a/roles/sftp_share/renovate.json b/roles/sftp_share/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/sftp_share/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/sftp_share/requirements.ci.txt b/roles/sftp_share/requirements.ci.txt new file mode 100644 index 0000000..f99c76a --- /dev/null +++ b/roles/sftp_share/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.31.0 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/sftp_share/requirements.txt b/roles/sftp_share/requirements.txt new file mode 100644 index 0000000..b7e1d8c --- /dev/null +++ b/roles/sftp_share/requirements.txt @@ -0,0 +1 @@ +ansible~=11.11.0 diff --git a/roles/sftp_share/requirements.yml b/roles/sftp_share/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/sftp_share/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/sftp_share/tasks/main.yml b/roles/sftp_share/tasks/main.yml new file mode 100644 index 0000000..f88a5a8 --- /dev/null +++ b/roles/sftp_share/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: Create SFTP user - {{ sftp_username }} + ansible.builtin.user: + name: "{{ sftp_username }}" + state: present + create_home: true + shell: /sbin/nologin + +- name: Create SFTP user upload directory - {{ sftp_username }} + ansible.builtin.file: + state: directory + path: "{{ sftp_root }}/{{ sftp_username }}-uploads/" + mode: '0755' + owner: root + group: root + +- name: Create transfers directory - {{ sftp_username }} + ansible.builtin.file: + state: directory + path: "{{ sftp_root }}/{{ sftp_username }}-uploads/transfers/" + mode: '2770' + owner: "{{ sftp_username }}" + group: "{{ sftp_transfers_groupname }}" + +- name: Push sshd config - {{ sftp_username }} + ansible.builtin.template: + src: sshd_config.conf.j2 + dest: "/etc/ssh/sshd_config.d/60-sftp-jail-{{ sftp_username }}.conf" + mode: '0600' + owner: "root" + group: "root" + notify: Restart sshd + +- name: Push public ssh key - {{ sftp_username }} + ansible.posix.authorized_key: + user: "{{ sftp_username }}" + key: "{{ sftp_pubkey }}" + state: present diff --git a/roles/sftp_share/templates/sshd_config.conf.j2 b/roles/sftp_share/templates/sshd_config.conf.j2 new file mode 100644 index 0000000..0496ef2 --- /dev/null +++ b/roles/sftp_share/templates/sshd_config.conf.j2 @@ -0,0 +1,5 @@ +Match User {{ sftp_username }} + ChrootDirectory /primary/data/{{ sftp_username }}-uploads + ForceCommand internal-sftp -u 007 -p {{ sftp_permissions }} -d /transfers + AllowTcpForwarding no + X11Forwarding no diff --git a/roles/sftp_share/vars/main.yml b/roles/sftp_share/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/sftp_share/vars/main.yml @@ -0,0 +1 @@ +--- From 8d3b9618f63553ee75cec3ffe29e4601c4a8ff92 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 18:15:47 +0300 Subject: [PATCH 79/96] fix ansible-lint errors --- roles/sftp_share/defaults/main.yml | 2 +- roles/sftp_share/meta/main.yml | 4 +-- .../sftp_share/molecule/default/converge.yml | 4 +-- roles/sftp_share/molecule/default/verify.yml | 36 +++++++++---------- .../sftp_share/templates/sshd_config.conf.j2 | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/roles/sftp_share/defaults/main.yml b/roles/sftp_share/defaults/main.yml index d742061..44355ef 100644 --- a/roles/sftp_share/defaults/main.yml +++ b/roles/sftp_share/defaults/main.yml @@ -1,2 +1,2 @@ --- -sftp_permissions: "open,close,read,write,lstat,fstat,opendir,readdir,remove,mkdir,rmdir,realpath,rename" +sftp_share_permissions: "open,close,read,write,lstat,fstat,opendir,readdir,remove,mkdir,rmdir,realpath,rename" diff --git a/roles/sftp_share/meta/main.yml b/roles/sftp_share/meta/main.yml index 06a754f..9d3f7e1 100644 --- a/roles/sftp_share/meta/main.yml +++ b/roles/sftp_share/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/sftp_share/molecule/default/converge.yml b/roles/sftp_share/molecule/default/converge.yml index a6cba4e..dee6099 100644 --- a/roles/sftp_share/molecule/default/converge.yml +++ b/roles/sftp_share/molecule/default/converge.yml @@ -31,9 +31,9 @@ group: root roles: - - role: genlab.sftp_share + - role: genlab.common.sftp_share sftp_username: "testusr" sftp_pubkey: "{{ lookup('file', 'ssh/id_ed25519.pub') }}" sftp_root: "/primary/data" sftp_transfers_groupname: "testgrp" - sftp_permissions: "open,close,read,lstat,fstat,opendir,readdir,realpath,stat" # read-only + sftp_share_permissions: "open,close,read,lstat,fstat,opendir,readdir,realpath,stat" # read-only diff --git a/roles/sftp_share/molecule/default/verify.yml b/roles/sftp_share/molecule/default/verify.yml index 43a853e..ebf4e65 100644 --- a/roles/sftp_share/molecule/default/verify.yml +++ b/roles/sftp_share/molecule/default/verify.yml @@ -13,67 +13,67 @@ - name: Gather user SFTP directory info ansible.builtin.stat: path: "{{ sftp_root }}/{{ sftp_username }}-uploads" - register: sftp_root_stat + register: sftp_share_permissions - name: Assert that user SFTP directory has correct owner ansible.builtin.assert: that: - - sftp_root_stat.stat.pw_name == "root" - - sftp_root_stat.stat.gr_name == "root" + - sftp_share_permissions.stat.pw_name == "root" + - sftp_share_permissions.stat.gr_name == "root" success_msg: "SFTP directory {{ sftp_root }}/{{ sftp_username }}-uploads has correct owner root:root" fail_msg: | "SFTP directory {{ sftp_root }}/{{ sftp_username }}-uploads does not have correct owner root:root, - actual owner is {{ sftp_root_stat.stat.pw_name }}:{{ sftp_root_stat.stat.gr_name }}" + actual owner is {{ sftp_share_permissions.stat.pw_name }}:{{ sftp_share_permissions.stat.gr_name }}" - name: Gather transfers directory info ansible.builtin.stat: path: "{{ sftp_root }}/{{ sftp_username }}-uploads/transfers" - register: transfers_stat + register: sftp_share_transfers_stat - name: Assert that transfers directory has correct owner ansible.builtin.assert: that: - - transfers_stat.stat.pw_name == sftp_username - - transfers_stat.stat.gr_name == sftp_transfers_groupname + - sftp_share_transfers_stat.stat.pw_name == sftp_username + - sftp_share_transfers_stat.stat.gr_name == sftp_transfers_groupname success_msg: "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers has correct owner {{ sftp_username }}:{{ sftp_transfers_groupname }}" fail_msg: | "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers does not have correct owner {{ sftp_username }}:{{ sftp_transfers_groupname }}, - actual owner is {{ transfers_stat.stat.pw_name }}:{{ transfers_stat.stat.gr_name }}" + actual owner is {{ sftp_share_transfers_stat.stat.pw_name }}:{{ sftp_share_transfers_stat.stat.gr_name }}" - name: Assert that transfers directory has correct permissions ansible.builtin.assert: that: - - transfers_stat.stat.mode == "2770" + - sftp_share_transfers_stat.stat.mode == "2770" success_msg: "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers has correct permissions 2770" fail_msg: | "Directory {{ sftp_root }}/{{ sftp_username }}-uploads/transfers does not have correct permissions 2770, - actual permissions are {{ sftp_root_stat.stat.mode }}" + actual permissions are {{ sftp_share_permissions.stat.mode }}" - name: Get user info ansible.builtin.getent: database: passwd key: "{{ sftp_username }}" - register: user_info + register: sftp_share_user_info - name: Check user exists ansible.builtin.assert: that: - - user_info is defined + - sftp_share_user_info is defined success_msg: "User {{ sftp_username }} exists" fail_msg: "User {{ sftp_username }} does not exist" - name: Check that sshd refuses non-SFTP connection attempts changed_when: false - failed_when: ssh.stdout != expected - register: ssh + failed_when: sftp_share_ssh.stdout != expected + register: sftp_share_ssh vars: expected: "This service allows sftp connections only." ansible.builtin.command: "ssh -l {{ sftp_username }} 127.0.0.1 -o StrictHostKeyChecking=no exit" - name: Check that sFTP chroots into /transfers changed_when: false - register: sftp - failed_when: sftp.stdout != expected + register: sftp_share_sftp + failed_when: sftp_share_sftp.stdout != expected vars: expected: "sftp> pwd\nRemote working directory: /transfers" ansible.builtin.shell: @@ -82,8 +82,8 @@ - name: Check that non-whitelisted sFTP permissions are denied changed_when: false - register: sftp_mkdir - failed_when: sftp_mkdir.stderr != expected + register: sftp_share_mkdir + failed_when: sftp_share_mkdir.stderr != expected vars: expected: 'remote mkdir "/transfers/foo": Permission denied' ansible.builtin.shell: diff --git a/roles/sftp_share/templates/sshd_config.conf.j2 b/roles/sftp_share/templates/sshd_config.conf.j2 index 0496ef2..f7dfdc8 100644 --- a/roles/sftp_share/templates/sshd_config.conf.j2 +++ b/roles/sftp_share/templates/sshd_config.conf.j2 @@ -1,5 +1,5 @@ Match User {{ sftp_username }} ChrootDirectory /primary/data/{{ sftp_username }}-uploads - ForceCommand internal-sftp -u 007 -p {{ sftp_permissions }} -d /transfers + ForceCommand internal-sftp -u 007 -p {{ sftp_share_permissions }} -d /transfers AllowTcpForwarding no X11Forwarding no From 26358fc4f4098c28ab7b9e5bb4387b454e18bfb1 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 18:16:36 +0300 Subject: [PATCH 80/96] remove garbage --- roles/sftp_share/.ansible-lint | 16 ---------------- roles/sftp_share/.gitignore | 3 --- roles/sftp_share/.yamllint | 8 -------- roles/sftp_share/conda.dev.yml | 11 ----------- roles/sftp_share/conda.prod.yml | 9 --------- roles/sftp_share/renovate.json | 6 ------ roles/sftp_share/requirements.ci.txt | 6 ------ roles/sftp_share/requirements.txt | 1 - roles/sftp_share/requirements.yml | 3 --- 9 files changed, 63 deletions(-) delete mode 100644 roles/sftp_share/.ansible-lint delete mode 100644 roles/sftp_share/.gitignore delete mode 100644 roles/sftp_share/.yamllint delete mode 100644 roles/sftp_share/conda.dev.yml delete mode 100644 roles/sftp_share/conda.prod.yml delete mode 100644 roles/sftp_share/renovate.json delete mode 100644 roles/sftp_share/requirements.ci.txt delete mode 100644 roles/sftp_share/requirements.txt delete mode 100644 roles/sftp_share/requirements.yml diff --git a/roles/sftp_share/.ansible-lint b/roles/sftp_share/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/sftp_share/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/sftp_share/.gitignore b/roles/sftp_share/.gitignore deleted file mode 100644 index 292f339..0000000 --- a/roles/sftp_share/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.vscode -.idea -.ansible \ No newline at end of file diff --git a/roles/sftp_share/.yamllint b/roles/sftp_share/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/sftp_share/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/sftp_share/conda.dev.yml b/roles/sftp_share/conda.dev.yml deleted file mode 100644 index 421257c..0000000 --- a/roles/sftp_share/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-sftp-share -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/sftp_share/conda.prod.yml b/roles/sftp_share/conda.prod.yml deleted file mode 100644 index 2f50d61..0000000 --- a/roles/sftp_share/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-sftp-share -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/sftp_share/renovate.json b/roles/sftp_share/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/sftp_share/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/sftp_share/requirements.ci.txt b/roles/sftp_share/requirements.ci.txt deleted file mode 100644 index f99c76a..0000000 --- a/roles/sftp_share/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.31.0 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/sftp_share/requirements.txt b/roles/sftp_share/requirements.txt deleted file mode 100644 index b7e1d8c..0000000 --- a/roles/sftp_share/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.11.0 diff --git a/roles/sftp_share/requirements.yml b/roles/sftp_share/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/sftp_share/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From feaf0b17adc3b6102dbb9b6285c9c450f0335a3e Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Tue, 16 Dec 2025 18:17:56 +0300 Subject: [PATCH 81/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 941083e..da62590 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ - [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) +- [sftp_share](roles/sftp_share/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) - [ufw](roles/ufw/README.md) - [wg_hub](roles/wg_hub/README.md) diff --git a/galaxy.yml b/galaxy.yml index 1b66324..0c316c6 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.16.0 +version: 0.17.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 7fd2c6d60aede037d4e0fed31e5ebff9ca4aaefc Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 11:20:05 +0300 Subject: [PATCH 82/96] add rustdesk role --- roles/rustdesk/.ansible-lint | 17 +++++ roles/rustdesk/.gitignore | 3 + roles/rustdesk/.yamllint | 8 +++ roles/rustdesk/README.md | 42 ++++++++++++ roles/rustdesk/conda.dev.yml | 11 +++ roles/rustdesk/conda.prod.yml | 9 +++ roles/rustdesk/defaults/main.yml | 3 + roles/rustdesk/files/generate-rustdesk-exe.sh | 9 +++ roles/rustdesk/files/rustdesk-hbbr.service | 32 +++++++++ roles/rustdesk/files/rustdesk-hbbs.service | 35 ++++++++++ roles/rustdesk/handlers/main.yml | 15 +++++ roles/rustdesk/meta/main.yml | 17 +++++ .../default/client-binary-existance.yml | 29 ++++++++ roles/rustdesk/molecule/default/converge.yml | 5 ++ roles/rustdesk/molecule/default/molecule.yml | 27 ++++++++ roles/rustdesk/molecule/default/verify.yml | 27 ++++++++ roles/rustdesk/renovate.json | 6 ++ roles/rustdesk/requirements.ci.txt | 6 ++ roles/rustdesk/requirements.txt | 1 + roles/rustdesk/requirements.yml | 3 + .../rustdesk/tasks/check-rustdesk-version.yml | 25 +++++++ roles/rustdesk/tasks/install-curl.yml | 30 +++++++++ roles/rustdesk/tasks/install-unzip.yml | 30 +++++++++ roles/rustdesk/tasks/main.yml | 67 +++++++++++++++++++ roles/rustdesk/vars/main.yml | 1 + 25 files changed, 458 insertions(+) create mode 100644 roles/rustdesk/.ansible-lint create mode 100644 roles/rustdesk/.gitignore create mode 100644 roles/rustdesk/.yamllint create mode 100644 roles/rustdesk/README.md create mode 100644 roles/rustdesk/conda.dev.yml create mode 100644 roles/rustdesk/conda.prod.yml create mode 100644 roles/rustdesk/defaults/main.yml create mode 100644 roles/rustdesk/files/generate-rustdesk-exe.sh create mode 100644 roles/rustdesk/files/rustdesk-hbbr.service create mode 100644 roles/rustdesk/files/rustdesk-hbbs.service create mode 100644 roles/rustdesk/handlers/main.yml create mode 100644 roles/rustdesk/meta/main.yml create mode 100644 roles/rustdesk/molecule/default/client-binary-existance.yml create mode 100644 roles/rustdesk/molecule/default/converge.yml create mode 100644 roles/rustdesk/molecule/default/molecule.yml create mode 100644 roles/rustdesk/molecule/default/verify.yml create mode 100644 roles/rustdesk/renovate.json create mode 100644 roles/rustdesk/requirements.ci.txt create mode 100644 roles/rustdesk/requirements.txt create mode 100644 roles/rustdesk/requirements.yml create mode 100644 roles/rustdesk/tasks/check-rustdesk-version.yml create mode 100644 roles/rustdesk/tasks/install-curl.yml create mode 100644 roles/rustdesk/tasks/install-unzip.yml create mode 100644 roles/rustdesk/tasks/main.yml create mode 100644 roles/rustdesk/vars/main.yml diff --git a/roles/rustdesk/.ansible-lint b/roles/rustdesk/.ansible-lint new file mode 100644 index 0000000..eee5545 --- /dev/null +++ b/roles/rustdesk/.ansible-lint @@ -0,0 +1,17 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ + - molecule/default/client-binary-existance.yml # ansible-lint thinks it's a playbook so gives an error, but it's tasks for include_task. decided to exclude it diff --git a/roles/rustdesk/.gitignore b/roles/rustdesk/.gitignore new file mode 100644 index 0000000..ce73723 --- /dev/null +++ b/roles/rustdesk/.gitignore @@ -0,0 +1,3 @@ +.vscode +.idea +.ansible diff --git a/roles/rustdesk/.yamllint b/roles/rustdesk/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/rustdesk/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/rustdesk/README.md b/roles/rustdesk/README.md new file mode 100644 index 0000000..390a16c --- /dev/null +++ b/roles/rustdesk/README.md @@ -0,0 +1,42 @@ +Rustdesk Server +========= + +Rustdesk server role for ansible + +Requirements +------------ + +Ports: +``` +tcp 21115, 21116, 21117, 21118, 21119 +udp 21116 +``` + +Role Variables +-------------- + +``` rustdesk_server_version ``` — contains rustdesk-server version to be installed +``` rustdesk_client_version ``` — contains rustdesk-client version for executable to be configured + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.rustdesk_server +``` + +License +------- + +BSD + +Author Information +------------------ + +malyuk.ss@genlab.llc diff --git a/roles/rustdesk/conda.dev.yml b/roles/rustdesk/conda.dev.yml new file mode 100644 index 0000000..f715e5e --- /dev/null +++ b/roles/rustdesk/conda.dev.yml @@ -0,0 +1,11 @@ +--- +name: ansible-rustdesk +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - actionlint + - pip: + - -r requirements.txt + - -r requirements.ci.txt diff --git a/roles/rustdesk/conda.prod.yml b/roles/rustdesk/conda.prod.yml new file mode 100644 index 0000000..ca9e8d5 --- /dev/null +++ b/roles/rustdesk/conda.prod.yml @@ -0,0 +1,9 @@ +--- +name: ansible-rustdesk +channels: + - conda-forge +dependencies: + - python~=3.12.0 + - pip>=24.2 + - pip: + - -r requirements.txt diff --git a/roles/rustdesk/defaults/main.yml b/roles/rustdesk/defaults/main.yml new file mode 100644 index 0000000..315c715 --- /dev/null +++ b/roles/rustdesk/defaults/main.yml @@ -0,0 +1,3 @@ +--- +rustdesk_server_version: "1.1.14" +rustdesk_client_version: "1.4.1" diff --git a/roles/rustdesk/files/generate-rustdesk-exe.sh b/roles/rustdesk/files/generate-rustdesk-exe.sh new file mode 100644 index 0000000..2fb454d --- /dev/null +++ b/roles/rustdesk/files/generate-rustdesk-exe.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# shellcheck disable=SC2155 +set -eu -o pipefail + +readonly RUSTDESK_SERVER_IP="$(curl -s ifconfig.me)" +readonly RUSTDESK_SERVER_KEY="$(cat /opt/rustdesk-server/lib/id_ed25519.pub)" + +cp /opt/rustdesk-server/lib/rustdesk-client.exe \ + /opt/rustdesk-server/lib/rustdesk-host="$RUSTDESK_SERVER_IP",key="$RUSTDESK_SERVER_KEY".exe diff --git a/roles/rustdesk/files/rustdesk-hbbr.service b/roles/rustdesk/files/rustdesk-hbbr.service new file mode 100644 index 0000000..6b0f672 --- /dev/null +++ b/roles/rustdesk/files/rustdesk-hbbr.service @@ -0,0 +1,32 @@ +[Unit] +Description=Rustdesk Relay Server + +[Service] +Type=simple +LimitNOFILE=10000 +Environment="RUST_LOG_LEVEL=trace" +WorkingDirectory=/opt/rustdesk-server/lib/ + +ExecStart=/opt/rustdesk-server/amd64/hbbr + +Restart=on-failure +RestartSec=10 +StartLimitIntervalSec=60 +StartLimitBurst=2 + +User=rustdesk +Group=rustdesk + +# Security +ProtectSystem=strict +ReadWritePaths=/opt/rustdesk-server/lib/ +NoNewPrivileges=true +PrivateTmp=true +ProtectKernelModules=true +ProtectControlGroups=true +ProtectKernelTunables=true +ProtectClock=yes +RestrictSUIDSGID=true + +[Install] +WantedBy=multi-user.target diff --git a/roles/rustdesk/files/rustdesk-hbbs.service b/roles/rustdesk/files/rustdesk-hbbs.service new file mode 100644 index 0000000..d84f44e --- /dev/null +++ b/roles/rustdesk/files/rustdesk-hbbs.service @@ -0,0 +1,35 @@ +[Unit] +Description=Rustdesk Signal Server + +[Service] +Type=simple +LimitNOFILE=10000 +Environment="RUST_LOG_LEVEL=trace" +WorkingDirectory=/opt/rustdesk-server/lib/ + +ExecStartPre=/bin/sh -c 'rm -rf /opt/rustdesk-server/lib/id_ed25519*' +ExecStartPre=/bin/sh -c 'rm -rf /opt/rustdesk-server/lib/rustdesk-host=*' +ExecStart=/opt/rustdesk-server/amd64/hbbs +ExecStartPost=/opt/rustdesk-server/generate-rustdesk-exe.sh + +Restart=on-failure +RestartSec=10 +StartLimitIntervalSec=60 +StartLimitBurst=2 + +User=rustdesk +Group=rustdesk + +# Security +ProtectSystem=strict +ReadWritePaths=/opt/rustdesk-server/lib/ +NoNewPrivileges=true +PrivateTmp=true +ProtectKernelModules=true +ProtectControlGroups=true +ProtectKernelTunables=true +ProtectClock=yes +RestrictSUIDSGID=true + +[Install] +WantedBy=multi-user.target diff --git a/roles/rustdesk/handlers/main.yml b/roles/rustdesk/handlers/main.yml new file mode 100644 index 0000000..976fccb --- /dev/null +++ b/roles/rustdesk/handlers/main.yml @@ -0,0 +1,15 @@ +--- + +- name: "Start and enable rustdesk-hbbr.service" + ansible.builtin.service: + name: "rustdesk-hbbr" + state: started + enabled: true + daemon_reload: true + +- name: "Start and enable rustdesk-hbbs.service" + ansible.builtin.service: + name: "rustdesk-hbbs" + state: started + enabled: true + daemon_reload: true diff --git a/roles/rustdesk/meta/main.yml b/roles/rustdesk/meta/main.yml new file mode 100644 index 0000000..4bd44b0 --- /dev/null +++ b/roles/rustdesk/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "rustdesk_server" + namespace: genlab + author: "Sergey Malyuk" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/rustdesk/molecule/default/client-binary-existance.yml b/roles/rustdesk/molecule/default/client-binary-existance.yml new file mode 100644 index 0000000..902a1aa --- /dev/null +++ b/roles/rustdesk/molecule/default/client-binary-existance.yml @@ -0,0 +1,29 @@ +--- +- name: "Get public IP" + register: public_ip + changed_when: false + ansible.builtin.uri: + url: https://ifconfig.me + return_content: true + headers: + Accept: text/plain + User-Agent: curl/7.68.0 + + +- name: "Get public key" + register: public_key + changed_when: false + ansible.builtin.command: "cat /opt/rustdesk-server/lib/id_ed25519.pub" + +- name: "Get client rustdesk binary information" + changed_when: false + register: rustdesk_bin + ansible.builtin.stat: + path: "/opt/rustdesk-server/lib/rustdesk-host={{ public_ip.content }},key={{ public_key.stdout }}.exe" + +- name: "Check if client binary exists" + changed_when: false + ansible.builtin.assert: + that: "rustdesk_bin.stat.exists" + success_msg: "Rustdesk binary exists and named correctly" + fail_msg: "Rustdesk binary not found" diff --git a/roles/rustdesk/molecule/default/converge.yml b/roles/rustdesk/molecule/default/converge.yml new file mode 100644 index 0000000..3299b75 --- /dev/null +++ b/roles/rustdesk/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: "genlab.rustdesk_server" diff --git a/roles/rustdesk/molecule/default/molecule.yml b/roles/rustdesk/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/rustdesk/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/rustdesk/molecule/default/verify.yml b/roles/rustdesk/molecule/default/verify.yml new file mode 100644 index 0000000..f4832ef --- /dev/null +++ b/roles/rustdesk/molecule/default/verify.yml @@ -0,0 +1,27 @@ +--- + +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars_files: + - ../../defaults/main.yml + + tasks: + - name: "Gather service facts" + ansible.builtin.service_facts: + + - name: "Check if rustdesk-hbbr is running" + ansible.builtin.assert: + that: "ansible_facts.services['rustdesk-hbbr.service'].state == 'running'" + success_msg: "rustdesk-hbbr is running" + fail_msg: "rustdesk-hbbr is NOT running" + + - name: "Check if rustdesk-hbbs is running" + ansible.builtin.assert: + that: "ansible_facts.services['rustdesk-hbbs.service'].state == 'running'" + success_msg: "rustdesk-hbbs is running" + fail_msg: "rustdesk-hbbs is NOT running" + + - name: "Check client binary existance" + ansible.builtin.include_tasks: client-binary-existance.yml diff --git a/roles/rustdesk/renovate.json b/roles/rustdesk/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/rustdesk/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/rustdesk/requirements.ci.txt b/roles/rustdesk/requirements.ci.txt new file mode 100644 index 0000000..debc892 --- /dev/null +++ b/roles/rustdesk/requirements.ci.txt @@ -0,0 +1,6 @@ +ansible-lint +molecule==24.12.0 +molecule-plugins[docker] +docker~=7.1.0 +requests==2.32.4 # pinned to the latest version not breaking Docker SDK +yamllint diff --git a/roles/rustdesk/requirements.txt b/roles/rustdesk/requirements.txt new file mode 100644 index 0000000..e8a6807 --- /dev/null +++ b/roles/rustdesk/requirements.txt @@ -0,0 +1 @@ +ansible~=11.7.0 diff --git a/roles/rustdesk/requirements.yml b/roles/rustdesk/requirements.yml new file mode 100644 index 0000000..3d5f1cd --- /dev/null +++ b/roles/rustdesk/requirements.yml @@ -0,0 +1,3 @@ +# requirements file +--- +collections: [] diff --git a/roles/rustdesk/tasks/check-rustdesk-version.yml b/roles/rustdesk/tasks/check-rustdesk-version.yml new file mode 100644 index 0000000..62cbb65 --- /dev/null +++ b/roles/rustdesk/tasks/check-rustdesk-version.yml @@ -0,0 +1,25 @@ +--- + +- name: "Check hbbr version" + register: hbbr_output + changed_when: false + ansible.builtin.command: + cmd: "/opt/rustdesk-server/amd64/hbbr --version" + +- name: "Check if the version of hbbr is the expected one" + ansible.builtin.assert: + that: "hbbr_output.stdout == 'hbbr {{ rustdesk_server_version }}'" + success_msg: "hbbr is installed and is the correct version" + fail_msg: "hbbr is either not installed or does not match the target version" + +- name: "Check hbbs version" + register: hbbs_output + changed_when: false + ansible.builtin.command: + cmd: "/opt/rustdesk-server/amd64/hbbs --version" + +- name: "Check if the version of hbbs is the expected one" + ansible.builtin.assert: + that: "hbbs_output.stdout == 'hbbs {{ rustdesk_server_version }}'" + success_msg: "hbbs is installed and is the correct version" + fail_msg: "hbbs is either not installed or does not match the target version" diff --git a/roles/rustdesk/tasks/install-curl.yml b/roles/rustdesk/tasks/install-curl.yml new file mode 100644 index 0000000..7729e18 --- /dev/null +++ b/roles/rustdesk/tasks/install-curl.yml @@ -0,0 +1,30 @@ +--- + +- name: "Install curl for generate-rustdesk-bat.sh script on Debian family" + when: ansible_os_family == "Debian" + ansible.builtin.apt: + name: curl + state: present + update_cache: true + +- name: "Install curl for generate-rustdesk-bat.sh script on RedHat family" + when: ansible_os_family == "RedHat" + ansible.builtin.dnf: + name: curl + state: present + +- name: "Install curl for generate-rustdesk-bat.sh script on FreeBSD family" + when: ansible_os_family == "FreeBSD" + community.general.pkgng: + name: curl + state: present + +- name: "Install curl for generate-rustdesk-bat.sh script on other OS" + when: + - ansible_os_family != "Debian" + - and ansible_os_family != "RedHat" + - and ansible_os_family != "FreeBSD" + ansible.builtin.package: + name: curl + state: present + update_cache: true diff --git a/roles/rustdesk/tasks/install-unzip.yml b/roles/rustdesk/tasks/install-unzip.yml new file mode 100644 index 0000000..efff4b1 --- /dev/null +++ b/roles/rustdesk/tasks/install-unzip.yml @@ -0,0 +1,30 @@ +--- + +- name: "Install unzip for unarchiving the binaries archive on Debian family" + when: ansible_os_family == "Debian" + ansible.builtin.apt: + name: unzip + state: present + update_cache: true + +- name: "Install unzip for unarchiving the binaries archive on RedHat family" + when: ansible_os_family == "RedHat" + ansible.builtin.dnf: + name: unzip + state: present + +- name: "Install unzip for unarchiving the binaries archive on FreeBSD family" + when: ansible_os_family == "FreeBSD" + community.general.pkgng: + name: unzip + state: present + +- name: "Install unzip for unarchiving the binaries archive on other OS" + when: + - ansible_os_family != "Debian" + - and ansible_os_family != "RedHat" + - and ansible_os_family != "FreeBSD" + ansible.builtin.package: + name: unzip + state: present + update_cache: true diff --git a/roles/rustdesk/tasks/main.yml b/roles/rustdesk/tasks/main.yml new file mode 100644 index 0000000..94a3998 --- /dev/null +++ b/roles/rustdesk/tasks/main.yml @@ -0,0 +1,67 @@ +--- + +- name: "Install unzip" + ansible.builtin.include_tasks: install-unzip.yml + +- name: "Install curl" + ansible.builtin.include_tasks: install-curl.yml + +- name: "Add user rustdesk" + ansible.builtin.user: + name: "rustdesk" + shell: /usr/sbin/nologin + create_home: false + +# Rustdesk uses /opt/rustdesk-server/lib/ as a working directory. When it's first started it generates key pairs and puts them in lib directory +- name: "Create lib directory" + ansible.builtin.file: + path: /opt/rustdesk-server/lib/ + state: directory + mode: "0755" + owner: "rustdesk" + group: "rustdesk" + +- name: "Download rustdesk-client" + ansible.builtin.get_url: + url: "https://github.com/rustdesk/rustdesk/releases/download/{{ rustdesk_client_version }}/rustdesk-{{ rustdesk_client_version }}-x86_64.exe" + dest: /opt/rustdesk-server/lib/rustdesk-client.exe + mode: "0644" + owner: "rustdesk" + group: "rustdesk" + +- name: "Copy generate-rustdesk-exe.sh to target machine" + ansible.builtin.copy: + src: "generate-rustdesk-exe.sh" + dest: /opt/rustdesk-server/ + mode: "0755" + owner: "rustdesk" + group: "rustdesk" + +- name: "Install rustdesk" + block: + - name: "Check rustdesk version" + ansible.builtin.include_tasks: check-rustdesk-version.yml + rescue: + - name: "Download and unarchive the binaries" + ansible.builtin.unarchive: + src: "https://github.com/rustdesk/rustdesk-server/releases/download/{{ rustdesk_server_version }}/rustdesk-server-linux-amd64.zip" + dest: /opt/rustdesk-server/ + remote_src: true + +- name: "Copy rustdesk-hbbr.service to /etc/systemd/system/" + notify: "Start and enable rustdesk-hbbr.service" + ansible.builtin.copy: + src: "rustdesk-hbbr.service" + dest: /etc/systemd/system/ + mode: "0755" + owner: "rustdesk" + group: "rustdesk" + +- name: "Copy rustdesk-hbbs.service to /etc/systemd/system/" + notify: "Start and enable rustdesk-hbbs.service" + ansible.builtin.copy: + src: "rustdesk-hbbs.service" + dest: /etc/systemd/system/ + mode: "0755" + owner: "rustdesk" + group: "rustdesk" diff --git a/roles/rustdesk/vars/main.yml b/roles/rustdesk/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/rustdesk/vars/main.yml @@ -0,0 +1 @@ +--- From dce2de214599c7e3c7d725e0c69e80db993a8fc9 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 12:26:46 +0400 Subject: [PATCH 83/96] Add the `node_exporter` role --- roles/node_exporter/README.md | 34 +++++++++++++++++ roles/node_exporter/defaults/main.yml | 2 + roles/node_exporter/handlers/main.yml | 1 + roles/node_exporter/meta/main.yml | 17 +++++++++ .../molecule/default/converge.yml | 6 +++ .../molecule/default/molecule.yml | 27 ++++++++++++++ .../node_exporter/molecule/default/verify.yml | 15 ++++++++ roles/node_exporter/tasks/install.yml | 37 +++++++++++++++++++ roles/node_exporter/tasks/main.yml | 6 +++ roles/node_exporter/tasks/run.yml | 8 ++++ .../templates/node_exporter.service.j2 | 12 ++++++ roles/node_exporter/vars/main.yml | 1 + 12 files changed, 166 insertions(+) create mode 100644 roles/node_exporter/README.md create mode 100644 roles/node_exporter/defaults/main.yml create mode 100644 roles/node_exporter/handlers/main.yml create mode 100644 roles/node_exporter/meta/main.yml create mode 100644 roles/node_exporter/molecule/default/converge.yml create mode 100644 roles/node_exporter/molecule/default/molecule.yml create mode 100644 roles/node_exporter/molecule/default/verify.yml create mode 100644 roles/node_exporter/tasks/install.yml create mode 100644 roles/node_exporter/tasks/main.yml create mode 100644 roles/node_exporter/tasks/run.yml create mode 100644 roles/node_exporter/templates/node_exporter.service.j2 create mode 100644 roles/node_exporter/vars/main.yml diff --git a/roles/node_exporter/README.md b/roles/node_exporter/README.md new file mode 100644 index 0000000..0ff7a86 --- /dev/null +++ b/roles/node_exporter/README.md @@ -0,0 +1,34 @@ +Role Name +========= + +Deploy `prometheus/node_exporter` binary as a systemd unit. + +Requirements +------------ + +None + +Role Variables +-------------- + +`node_exporter_bin_path`: where to install the binary (default: `/usr/bin`). + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See: [converge.yml](molecule/default/converge.yml) + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/node_exporter/defaults/main.yml b/roles/node_exporter/defaults/main.yml new file mode 100644 index 0000000..daf4778 --- /dev/null +++ b/roles/node_exporter/defaults/main.yml @@ -0,0 +1,2 @@ +--- +node_exporter_bin_path: /usr/bin diff --git a/roles/node_exporter/handlers/main.yml b/roles/node_exporter/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/node_exporter/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/node_exporter/meta/main.yml b/roles/node_exporter/meta/main.yml new file mode 100644 index 0000000..885b94c --- /dev/null +++ b/roles/node_exporter/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: node_exporter + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Deploy prometheus/node_exporter binary as a systemd unit" + license: "GPL-2.0-or-later" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy"] + + galaxy_tags: ["prometheus"] + +dependencies: [] diff --git a/roles/node_exporter/molecule/default/converge.yml b/roles/node_exporter/molecule/default/converge.yml new file mode 100644 index 0000000..e0158f0 --- /dev/null +++ b/roles/node_exporter/molecule/default/converge.yml @@ -0,0 +1,6 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.node_exporter + node_exporter_ver: "1.10.1" diff --git a/roles/node_exporter/molecule/default/molecule.yml b/roles/node_exporter/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/node_exporter/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml new file mode 100644 index 0000000..b923a42 --- /dev/null +++ b/roles/node_exporter/molecule/default/verify.yml @@ -0,0 +1,15 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + tasks: + + - name: "Sanity check the node exporter" + retries: 5 + delay: 1 + register: result + failed_when: '"node_exporter_build_info" not in result.content' + ansible.builtin.uri: + url: "http://localhost:9100/metrics" + return_content: true diff --git a/roles/node_exporter/tasks/install.yml b/roles/node_exporter/tasks/install.yml new file mode 100644 index 0000000..7a0ab83 --- /dev/null +++ b/roles/node_exporter/tasks/install.yml @@ -0,0 +1,37 @@ +--- +- name: "Install | Check if node_exporter is accessible from $PATH" + changed_when: false + failed_when: false + register: cmd_node_exporter + ansible.builtin.command: + cmd: node_exporter --version + +- name: "Install | Build the exporter name" + when: "node_exporter_ver not in cmd_node_exporter.stdout" + ansible.builtin.set_fact: + prom_exp_name: "/node_exporter-{{ node_exporter_ver }}\ + .{{ ansible_system | lower }}-\ + {{ (ansible_architecture == 'x86_64') | ternary('amd64', ansible_architecture) }}" + +- name: "Install | Download the node_exporter binary (release {{ node_exporter_ver }})" + when: "node_exporter_ver not in cmd_node_exporter.stdout" + ansible.builtin.unarchive: + src: "\ + https://github.com/prometheus/node_exporter/releases/download/\ + v{{ node_exporter_ver }}\ + {{ prom_exp_name }}\ + .tar.gz" + dest: /tmp + remote_src: true + +- name: "Install | Install the downloaded binary to '{{ node_exporter_bin_path }}'" + when: "node_exporter_ver not in cmd_node_exporter.stdout" + changed_when: false + ansible.builtin.command: + cmd: install /tmp/{{ prom_exp_name }}/node_exporter -t {{ node_exporter_bin_path }} + +- name: "Install | Push the service file template" + ansible.builtin.template: + src: node_exporter.service.j2 + dest: /usr/lib/systemd/system/node_exporter.service + mode: "0660" diff --git a/roles/node_exporter/tasks/main.yml b/roles/node_exporter/tasks/main.yml new file mode 100644 index 0000000..b63f5a3 --- /dev/null +++ b/roles/node_exporter/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: "Include installation tasks" + ansible.builtin.include_tasks: "install.yml" + +- name: "Including run tasks" + ansible.builtin.include_tasks: "run.yml" diff --git a/roles/node_exporter/tasks/run.yml b/roles/node_exporter/tasks/run.yml new file mode 100644 index 0000000..2d71c2e --- /dev/null +++ b/roles/node_exporter/tasks/run.yml @@ -0,0 +1,8 @@ +--- +- name: "Enable and start the node_exporter service" + changed_when: false + ansible.builtin.systemd: + name: node_exporter + enabled: true + daemon_reload: true + state: restarted diff --git a/roles/node_exporter/templates/node_exporter.service.j2 b/roles/node_exporter/templates/node_exporter.service.j2 new file mode 100644 index 0000000..414c5c6 --- /dev/null +++ b/roles/node_exporter/templates/node_exporter.service.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Node Exporter +After=network.target + +[Service] +Type=simple +ExecStart={{ node_exporter_bin_path }}/node_exporter \ + --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/) \ + --collector.systemd + +[Install] +WantedBy=multi-user.target diff --git a/roles/node_exporter/vars/main.yml b/roles/node_exporter/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/node_exporter/vars/main.yml @@ -0,0 +1 @@ +--- From 509e19bd5b383e31ebd2ded619c0afb0dd885b42 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 11:37:16 +0300 Subject: [PATCH 84/96] fix ansible-lint errors --- .ansible-lint | 2 ++ roles/rustdesk/.ansible-lint | 2 +- roles/rustdesk/meta/main.yml | 6 +++--- .../molecule/default/client-binary-existance.yml | 9 ++++----- roles/rustdesk/molecule/default/converge.yml | 2 +- roles/rustdesk/tasks/check-rustdesk-version.yml | 8 ++++---- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 38f5606..f7c8aa6 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -8,3 +8,5 @@ loop_var_prefix: "^(__|{role}_)" exclude_paths: - .github/ - .ansible/ + # ansible-lint thinks it's a playbook so gives an error, but its tasks are for include_task. decided to exclude it + - roles/rustdesk/molecule/default/client-binary-existance.yml diff --git a/roles/rustdesk/.ansible-lint b/roles/rustdesk/.ansible-lint index eee5545..504a9b8 100644 --- a/roles/rustdesk/.ansible-lint +++ b/roles/rustdesk/.ansible-lint @@ -14,4 +14,4 @@ warn_list: exclude_paths: - .github/ - - molecule/default/client-binary-existance.yml # ansible-lint thinks it's a playbook so gives an error, but it's tasks for include_task. decided to exclude it + - molecule/default/client-binary-existance.yml # ansible-lint thinks it's a playbook so gives an error, but its tasks for include_task. decided to exclude it diff --git a/roles/rustdesk/meta/main.yml b/roles/rustdesk/meta/main.yml index 4bd44b0..d396088 100644 --- a/roles/rustdesk/meta/main.yml +++ b/roles/rustdesk/meta/main.yml @@ -1,6 +1,6 @@ --- galaxy_info: - role_name: "rustdesk_server" + role_name: "rustdesk" namespace: genlab author: "Sergey Malyuk" company: "Genlab, LLC" @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/rustdesk/molecule/default/client-binary-existance.yml b/roles/rustdesk/molecule/default/client-binary-existance.yml index 902a1aa..f1f76f8 100644 --- a/roles/rustdesk/molecule/default/client-binary-existance.yml +++ b/roles/rustdesk/molecule/default/client-binary-existance.yml @@ -1,6 +1,6 @@ --- - name: "Get public IP" - register: public_ip + register: rustdesk_public_ip changed_when: false ansible.builtin.uri: url: https://ifconfig.me @@ -9,17 +9,16 @@ Accept: text/plain User-Agent: curl/7.68.0 - - name: "Get public key" - register: public_key + register: rustdesk_public_key changed_when: false ansible.builtin.command: "cat /opt/rustdesk-server/lib/id_ed25519.pub" - + - name: "Get client rustdesk binary information" changed_when: false register: rustdesk_bin ansible.builtin.stat: - path: "/opt/rustdesk-server/lib/rustdesk-host={{ public_ip.content }},key={{ public_key.stdout }}.exe" + path: "/opt/rustdesk-server/lib/rustdesk-host={{ rustdesk_public_ip.content }},key={{ rustdesk_public_key.stdout }}.exe" - name: "Check if client binary exists" changed_when: false diff --git a/roles/rustdesk/molecule/default/converge.yml b/roles/rustdesk/molecule/default/converge.yml index 3299b75..66aeafe 100644 --- a/roles/rustdesk/molecule/default/converge.yml +++ b/roles/rustdesk/molecule/default/converge.yml @@ -2,4 +2,4 @@ - name: Converge hosts: all roles: - - role: "genlab.rustdesk_server" + - role: "genlab.common.rustdesk" diff --git a/roles/rustdesk/tasks/check-rustdesk-version.yml b/roles/rustdesk/tasks/check-rustdesk-version.yml index 62cbb65..776b702 100644 --- a/roles/rustdesk/tasks/check-rustdesk-version.yml +++ b/roles/rustdesk/tasks/check-rustdesk-version.yml @@ -1,25 +1,25 @@ --- - name: "Check hbbr version" - register: hbbr_output + register: rustdesk_hbbr_output changed_when: false ansible.builtin.command: cmd: "/opt/rustdesk-server/amd64/hbbr --version" - name: "Check if the version of hbbr is the expected one" ansible.builtin.assert: - that: "hbbr_output.stdout == 'hbbr {{ rustdesk_server_version }}'" + that: "rustdesk_hbbr_output.stdout == 'hbbr {{ rustdesk_server_version }}'" success_msg: "hbbr is installed and is the correct version" fail_msg: "hbbr is either not installed or does not match the target version" - name: "Check hbbs version" - register: hbbs_output + register: rustdesk_hbbs_output changed_when: false ansible.builtin.command: cmd: "/opt/rustdesk-server/amd64/hbbs --version" - name: "Check if the version of hbbs is the expected one" ansible.builtin.assert: - that: "hbbs_output.stdout == 'hbbs {{ rustdesk_server_version }}'" + that: "rustdesk_hbbs_output.stdout == 'hbbs {{ rustdesk_server_version }}'" success_msg: "hbbs is installed and is the correct version" fail_msg: "hbbs is either not installed or does not match the target version" From 29ca89ed93ec834589b0ea66c3d44e077910e0c1 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 12:38:30 +0400 Subject: [PATCH 85/96] Disable an irrelevant KICS warning about unsafe URLs --- roles/node_exporter/molecule/default/verify.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml index b923a42..e04b1a0 100644 --- a/roles/node_exporter/molecule/default/verify.yml +++ b/roles/node_exporter/molecule/default/verify.yml @@ -1,10 +1,11 @@ +# kics-scan-disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 --- - name: Verify hosts: all gather_facts: false any_errors_fatal: true + tasks: - - name: "Sanity check the node exporter" retries: 5 delay: 1 From 3ea70738962425ec345b4189735a6199512031bb Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 12:38:44 +0400 Subject: [PATCH 86/96] Rename variables according to the convention --- roles/node_exporter/tasks/install.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/node_exporter/tasks/install.yml b/roles/node_exporter/tasks/install.yml index 7a0ab83..aaff138 100644 --- a/roles/node_exporter/tasks/install.yml +++ b/roles/node_exporter/tasks/install.yml @@ -2,33 +2,33 @@ - name: "Install | Check if node_exporter is accessible from $PATH" changed_when: false failed_when: false - register: cmd_node_exporter + register: node_exporter_cmd ansible.builtin.command: cmd: node_exporter --version - name: "Install | Build the exporter name" - when: "node_exporter_ver not in cmd_node_exporter.stdout" + when: "node_exporter_ver not in node_exporter_cmd.stdout" ansible.builtin.set_fact: - prom_exp_name: "/node_exporter-{{ node_exporter_ver }}\ + node_exporter_name: "/node_exporter-{{ node_exporter_ver }}\ .{{ ansible_system | lower }}-\ {{ (ansible_architecture == 'x86_64') | ternary('amd64', ansible_architecture) }}" - name: "Install | Download the node_exporter binary (release {{ node_exporter_ver }})" - when: "node_exporter_ver not in cmd_node_exporter.stdout" + when: "node_exporter_ver not in node_exporter_cmd.stdout" ansible.builtin.unarchive: src: "\ https://github.com/prometheus/node_exporter/releases/download/\ v{{ node_exporter_ver }}\ - {{ prom_exp_name }}\ + {{ node_exporter_name }}\ .tar.gz" dest: /tmp remote_src: true - name: "Install | Install the downloaded binary to '{{ node_exporter_bin_path }}'" - when: "node_exporter_ver not in cmd_node_exporter.stdout" + when: "node_exporter_ver not in node_exporter_cmd.stdout" changed_when: false ansible.builtin.command: - cmd: install /tmp/{{ prom_exp_name }}/node_exporter -t {{ node_exporter_bin_path }} + cmd: install /tmp/{{ node_exporter_name }}/node_exporter -t {{ node_exporter_bin_path }} - name: "Install | Push the service file template" ansible.builtin.template: From bb41f1ee4bf7bbe844b8c8b1f63f41d372508e9f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 11:39:30 +0300 Subject: [PATCH 87/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da62590..f383011 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ - [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) +- [rustdesk](roles/rustdesk/README.md) - [sftp_share](roles/sftp_share/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) - [ufw](roles/ufw/README.md) diff --git a/galaxy.yml b/galaxy.yml index 0c316c6..a760250 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.17.0 +version: 0.18.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From cc8960ec92c3ba768d92f55d2a1d380f6475bd48 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 11:42:02 +0300 Subject: [PATCH 88/96] remove files that are no longer used --- roles/rustdesk/.ansible-lint | 17 ----------------- roles/rustdesk/.gitignore | 3 --- roles/rustdesk/.yamllint | 8 -------- roles/rustdesk/conda.dev.yml | 11 ----------- roles/rustdesk/conda.prod.yml | 9 --------- roles/rustdesk/renovate.json | 6 ------ roles/rustdesk/requirements.ci.txt | 6 ------ roles/rustdesk/requirements.txt | 1 - roles/rustdesk/requirements.yml | 3 --- 9 files changed, 64 deletions(-) delete mode 100644 roles/rustdesk/.ansible-lint delete mode 100644 roles/rustdesk/.gitignore delete mode 100644 roles/rustdesk/.yamllint delete mode 100644 roles/rustdesk/conda.dev.yml delete mode 100644 roles/rustdesk/conda.prod.yml delete mode 100644 roles/rustdesk/renovate.json delete mode 100644 roles/rustdesk/requirements.ci.txt delete mode 100644 roles/rustdesk/requirements.txt delete mode 100644 roles/rustdesk/requirements.yml diff --git a/roles/rustdesk/.ansible-lint b/roles/rustdesk/.ansible-lint deleted file mode 100644 index 504a9b8..0000000 --- a/roles/rustdesk/.ansible-lint +++ /dev/null @@ -1,17 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ - - molecule/default/client-binary-existance.yml # ansible-lint thinks it's a playbook so gives an error, but its tasks for include_task. decided to exclude it diff --git a/roles/rustdesk/.gitignore b/roles/rustdesk/.gitignore deleted file mode 100644 index ce73723..0000000 --- a/roles/rustdesk/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.vscode -.idea -.ansible diff --git a/roles/rustdesk/.yamllint b/roles/rustdesk/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/rustdesk/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/rustdesk/conda.dev.yml b/roles/rustdesk/conda.dev.yml deleted file mode 100644 index f715e5e..0000000 --- a/roles/rustdesk/conda.dev.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: ansible-rustdesk -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - actionlint - - pip: - - -r requirements.txt - - -r requirements.ci.txt diff --git a/roles/rustdesk/conda.prod.yml b/roles/rustdesk/conda.prod.yml deleted file mode 100644 index ca9e8d5..0000000 --- a/roles/rustdesk/conda.prod.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: ansible-rustdesk -channels: - - conda-forge -dependencies: - - python~=3.12.0 - - pip>=24.2 - - pip: - - -r requirements.txt diff --git a/roles/rustdesk/renovate.json b/roles/rustdesk/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/rustdesk/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/rustdesk/requirements.ci.txt b/roles/rustdesk/requirements.ci.txt deleted file mode 100644 index debc892..0000000 --- a/roles/rustdesk/requirements.ci.txt +++ /dev/null @@ -1,6 +0,0 @@ -ansible-lint -molecule==24.12.0 -molecule-plugins[docker] -docker~=7.1.0 -requests==2.32.4 # pinned to the latest version not breaking Docker SDK -yamllint diff --git a/roles/rustdesk/requirements.txt b/roles/rustdesk/requirements.txt deleted file mode 100644 index e8a6807..0000000 --- a/roles/rustdesk/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ansible~=11.7.0 diff --git a/roles/rustdesk/requirements.yml b/roles/rustdesk/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/rustdesk/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From c6f604fcc65a58c0cb4c377002c71430645d2e26 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 13:47:47 +0400 Subject: [PATCH 89/96] Add more linting fixes --- roles/node_exporter/molecule/default/verify.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml index e04b1a0..5687a6d 100644 --- a/roles/node_exporter/molecule/default/verify.yml +++ b/roles/node_exporter/molecule/default/verify.yml @@ -4,13 +4,13 @@ hosts: all gather_facts: false any_errors_fatal: true - + tasks: - name: "Sanity check the node exporter" retries: 5 delay: 1 - register: result - failed_when: '"node_exporter_build_info" not in result.content' + register: node_exporter_check + failed_when: '"node_exporter_build_info" not in node_exporter_check.content' ansible.builtin.uri: url: "http://localhost:9100/metrics" return_content: true From a5b6f0ff75520b115f11cdf329c469552bfef326 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 13:48:15 +0400 Subject: [PATCH 90/96] Fix KICS rule disabling syntax --- roles/node_exporter/molecule/default/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml index 5687a6d..3f1bb0b 100644 --- a/roles/node_exporter/molecule/default/verify.yml +++ b/roles/node_exporter/molecule/default/verify.yml @@ -1,4 +1,4 @@ -# kics-scan-disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 +# kics-scan disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 --- - name: Verify hosts: all From 5b6ecbd32d2a1aeaca788e2ec4f86ef14632d9b2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 17:58:14 +0300 Subject: [PATCH 91/96] add promtail role --- roles/promtail/.ansible-lint | 16 +++ roles/promtail/.gitignore | 2 + roles/promtail/.yamllint | 8 ++ roles/promtail/README.md | 44 ++++++++ roles/promtail/defaults/main.yml | 7 ++ roles/promtail/handlers/main.yml | 11 ++ roles/promtail/meta/main.yml | 14 +++ roles/promtail/molecule/default/converge.yml | 5 + roles/promtail/molecule/default/molecule.yml | 24 +++++ roles/promtail/molecule/default/verify.yml | 19 ++++ roles/promtail/renovate.json | 6 ++ roles/promtail/requirements.ci.txt | 5 + roles/promtail/tasks/main.yml | 107 +++++++++++++++++++ roles/promtail/templates/promtail.service.j2 | 11 ++ roles/promtail/templates/promtail.yml.j2 | 28 +++++ roles/promtail/vars/main.yml | 1 + 16 files changed, 308 insertions(+) create mode 100644 roles/promtail/.ansible-lint create mode 100644 roles/promtail/.gitignore create mode 100644 roles/promtail/.yamllint create mode 100644 roles/promtail/README.md create mode 100644 roles/promtail/defaults/main.yml create mode 100644 roles/promtail/handlers/main.yml create mode 100644 roles/promtail/meta/main.yml create mode 100644 roles/promtail/molecule/default/converge.yml create mode 100644 roles/promtail/molecule/default/molecule.yml create mode 100644 roles/promtail/molecule/default/verify.yml create mode 100644 roles/promtail/renovate.json create mode 100644 roles/promtail/requirements.ci.txt create mode 100644 roles/promtail/tasks/main.yml create mode 100644 roles/promtail/templates/promtail.service.j2 create mode 100644 roles/promtail/templates/promtail.yml.j2 create mode 100644 roles/promtail/vars/main.yml diff --git a/roles/promtail/.ansible-lint b/roles/promtail/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/promtail/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/promtail/.gitignore b/roles/promtail/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/promtail/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/promtail/.yamllint b/roles/promtail/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/promtail/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/promtail/README.md b/roles/promtail/README.md new file mode 100644 index 0000000..ee40a4f --- /dev/null +++ b/roles/promtail/README.md @@ -0,0 +1,44 @@ +promtail +========= + +Installs promtail as systemd service. + +Requirements +------------ + +Role Variables +-------------- + +(all optional) +`promtail_version`: version to install +`loki_server`: set loki server +`loki_port`: set loki port +`custom_server_config`: path to custom config witch replace all config with your own +`custom_scrape_configs`: path to custom scrape configs + +Dependencies +------------ + +No + +Example Playbook +---------------- + + - hosts: servers + roles: + - role: promtail + promtail_version: 2.7.3 + +License +------- + +MIT + +Author Information +------------------ + +Alexander Gorelyshev and Danilkin Danila (MIPT) + +Genlab LLC + +corvus-migratorius@proton.me diff --git a/roles/promtail/defaults/main.yml b/roles/promtail/defaults/main.yml new file mode 100644 index 0000000..5a64778 --- /dev/null +++ b/roles/promtail/defaults/main.yml @@ -0,0 +1,7 @@ +--- +promtail_version: 2.7.3 +promtail_positions_path: /home/promtail/positions.yaml +promtail_http_port: 9080 +loki_server: localhost +loki_port: 3100 +add_var_logs: true diff --git a/roles/promtail/handlers/main.yml b/roles/promtail/handlers/main.yml new file mode 100644 index 0000000..d1c06ac --- /dev/null +++ b/roles/promtail/handlers/main.yml @@ -0,0 +1,11 @@ +--- + +- name: "Restart the Promtail daemon" + ansible.builtin.systemd: + name: promtail + state: restarted + + +- name: "Reload the Promtail daemon configuration" + ansible.builtin.systemd: + daemon_reload: true diff --git a/roles/promtail/meta/main.yml b/roles/promtail/meta/main.yml new file mode 100644 index 0000000..79d6be3 --- /dev/null +++ b/roles/promtail/meta/main.yml @@ -0,0 +1,14 @@ +galaxy_info: + role_name: promtail + namespace: genlab + author: Danilkin Danila + description: Installs Promtail + + company: Genlab LLC + license: MIT + + min_ansible_version: "2.1" + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/promtail/molecule/default/converge.yml b/roles/promtail/molecule/default/converge.yml new file mode 100644 index 0000000..ea072dd --- /dev/null +++ b/roles/promtail/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.promtail diff --git a/roles/promtail/molecule/default/molecule.yml b/roles/promtail/molecule/default/molecule.yml new file mode 100644 index 0000000..8d39b18 --- /dev/null +++ b/roles/promtail/molecule/default/molecule.yml @@ -0,0 +1,24 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: ubuntu + image: geerlingguy/docker-ubuntu2204-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true +provisioner: + name: ansible + playbooks: + converge: converge.yml +verifier: + name: ansible +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/promtail/molecule/default/verify.yml b/roles/promtail/molecule/default/verify.yml new file mode 100644 index 0000000..c9ef143 --- /dev/null +++ b/roles/promtail/molecule/default/verify.yml @@ -0,0 +1,19 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # https://github.com/ansible/molecule/issues/3587#issuecomment-1158650179 + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Sanity check the Promtail daemon" + retries: 3 + delay: 1 + register: this + failed_when: "this.content != 'Ready'" + ansible.builtin.uri: + url: "http://localhost:{{ promtail_http_port }}/ready" + return_content: true diff --git a/roles/promtail/renovate.json b/roles/promtail/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/promtail/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/promtail/requirements.ci.txt b/roles/promtail/requirements.ci.txt new file mode 100644 index 0000000..5eabc75 --- /dev/null +++ b/roles/promtail/requirements.ci.txt @@ -0,0 +1,5 @@ +ansible +molecule +molecule-plugins[docker] +docker +requests==2.31.0 # pinned to the latest version not breaking Docker SDK \ No newline at end of file diff --git a/roles/promtail/tasks/main.yml b/roles/promtail/tasks/main.yml new file mode 100644 index 0000000..ca1458b --- /dev/null +++ b/roles/promtail/tasks/main.yml @@ -0,0 +1,107 @@ +--- +- name: "Install packages" + ansible.builtin.apt: + name: [ acl, unzip ] + state: present + update_cache: true + cache_valid_time: 3600 + + +- name: "Create the 'promtail' group" + ansible.builtin.group: + name: promtail + state: present + + +- name: "Create the 'promtail' user" + ansible.builtin.user: + name: promtail + groups: + - promtail + - systemd-journal + - adm + state: present + system: true + + +- name: "Set facl to allow promtail user to read '/var/log' contents" + ansible.posix.acl: + path: /var/log + entity: promtail + etype: user + permissions: rX + state: present + + +- name: "Make promtail user owner of '{{ promtail_positions_path }}'" + changed_when: false + ansible.builtin.file: + path: "{{ promtail_positions_path }}" + owner: promtail + group: promtail + state: touch + mode: "0750" + + +- name: "Install the requested Promtail version" + block: + - name: "Check Promtail version" + ansible.builtin.command: /usr/bin/promtail --version + register: promtail_version_check + changed_when: false + + - name: "Assert version correctness" + notify: "Restart the Promtail daemon" + ansible.builtin.assert: + that: "promtail_version in promtail_version_check.stdout" + success_msg: "Expected Promtail version available ({{ promtail_version }})" + fail_msg: "Expected version '{{ promtail_version }}'; available is '{{ promtail_version_check.stdout }}'" + + rescue: + - name: "Install Promtail if its not present" + ansible.builtin.unarchive: + src: "https://github.com/grafana/loki/releases/download/v\ + {{ promtail_version }}/promtail-linux-amd64.zip" + dest: /usr/bin/ + remote_src: true + + - name: "Rename and set permissions for the Promtail binary" + ansible.builtin.copy: + src: "/usr/bin/promtail-linux-amd64" + dest: "/usr/bin/promtail" + owner: root + group: root + mode: "0755" + remote_src: true + + - name: "Cleanup the downloaded file" + ansible.builtin.file: + path: "/usr/bin/promtail-linux-amd64" + state: absent + + +- name: "Template the systemd unit file" + notify: "Reload the Promtail daemon configuration" + ansible.builtin.template: + src: promtail.service.j2 + dest: /etc/systemd/system/promtail.service + owner: root + group: root + mode: "0644" + + +- name: "Template Promtail config file" + notify: "Restart the Promtail daemon" + ansible.builtin.template: + src: templates/promtail.yml.j2 + dest: /usr/local/bin/config-promtail.yml + owner: root + group: root + mode: "0644" + + +- name: "Enable and start Promtail daemon" + ansible.builtin.systemd: + name: promtail + state: started + enabled: true diff --git a/roles/promtail/templates/promtail.service.j2 b/roles/promtail/templates/promtail.service.j2 new file mode 100644 index 0000000..3b7b517 --- /dev/null +++ b/roles/promtail/templates/promtail.service.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Promtail service +After=network.target + +[Service] +Type=simple +User=promtail +ExecStart=/usr/bin/promtail -config.file /usr/local/bin/config-promtail.yml + +[Install] +WantedBy=multi-user.target diff --git a/roles/promtail/templates/promtail.yml.j2 b/roles/promtail/templates/promtail.yml.j2 new file mode 100644 index 0000000..2435643 --- /dev/null +++ b/roles/promtail/templates/promtail.yml.j2 @@ -0,0 +1,28 @@ +{% if custom_server_config is defined %} +{{ custom_server_config }} +{% else %} +server: + http_listen_port: {{ promtail_http_port }} + grpc_listen_port: 0 +{% endif %} +positions: + filename: {{ promtail_positions_path }} + +clients: + - url: "http://{{ loki_server }}:{{ loki_port }}/loki/api/v1/push" + +scrape_configs: +{% if add_var_logs %} + - job_name: system + static_configs: + - targets: + - localhost + labels: + job: varlogs + __path__: /var/log/*log + host: {{ ansible_hostname }} +{% endif %} + +{% if custom_scrape_configs is defined %} +{{ custom_scrape_configs }} +{% endif %} diff --git a/roles/promtail/vars/main.yml b/roles/promtail/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/promtail/vars/main.yml @@ -0,0 +1 @@ +--- From d89bc09b92fc74a43e4da4d409e4c2340c37eb04 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:06:01 +0300 Subject: [PATCH 92/96] fix ansible-lint errors --- roles/promtail/README.md | 4 ++-- roles/promtail/defaults/main.yml | 6 +++--- roles/promtail/molecule/default/verify.yml | 6 +++--- roles/promtail/tasks/main.yml | 2 +- roles/promtail/templates/promtail.yml.j2 | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/roles/promtail/README.md b/roles/promtail/README.md index ee40a4f..7dac0b4 100644 --- a/roles/promtail/README.md +++ b/roles/promtail/README.md @@ -11,8 +11,8 @@ Role Variables (all optional) `promtail_version`: version to install -`loki_server`: set loki server -`loki_port`: set loki port +`promtail_loki_server`: set loki server +`promtail_loki_port`: set loki port `custom_server_config`: path to custom config witch replace all config with your own `custom_scrape_configs`: path to custom scrape configs diff --git a/roles/promtail/defaults/main.yml b/roles/promtail/defaults/main.yml index 5a64778..8a8fa39 100644 --- a/roles/promtail/defaults/main.yml +++ b/roles/promtail/defaults/main.yml @@ -2,6 +2,6 @@ promtail_version: 2.7.3 promtail_positions_path: /home/promtail/positions.yaml promtail_http_port: 9080 -loki_server: localhost -loki_port: 3100 -add_var_logs: true +promtail_loki_server: localhost +promtail_loki_port: 3100 +promtail_add_var_logs: true diff --git a/roles/promtail/molecule/default/verify.yml b/roles/promtail/molecule/default/verify.yml index c9ef143..e1e0984 100644 --- a/roles/promtail/molecule/default/verify.yml +++ b/roles/promtail/molecule/default/verify.yml @@ -7,13 +7,13 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Sanity check the Promtail daemon" retries: 3 delay: 1 - register: this - failed_when: "this.content != 'Ready'" + register: promtail_this + failed_when: "promtail_this.content != 'Ready'" ansible.builtin.uri: url: "http://localhost:{{ promtail_http_port }}/ready" return_content: true diff --git a/roles/promtail/tasks/main.yml b/roles/promtail/tasks/main.yml index ca1458b..127c10c 100644 --- a/roles/promtail/tasks/main.yml +++ b/roles/promtail/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: "Install packages" ansible.builtin.apt: - name: [ acl, unzip ] + name: [acl, unzip] state: present update_cache: true cache_valid_time: 3600 diff --git a/roles/promtail/templates/promtail.yml.j2 b/roles/promtail/templates/promtail.yml.j2 index 2435643..21dea49 100644 --- a/roles/promtail/templates/promtail.yml.j2 +++ b/roles/promtail/templates/promtail.yml.j2 @@ -9,10 +9,10 @@ positions: filename: {{ promtail_positions_path }} clients: - - url: "http://{{ loki_server }}:{{ loki_port }}/loki/api/v1/push" + - url: "http://{{ promtail_loki_server }}:{{ promtail_loki_port }}/loki/api/v1/push" scrape_configs: -{% if add_var_logs %} +{% if promtail_add_var_logs %} - job_name: system static_configs: - targets: From a683f014636140abe51aa44364865e577021295b Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:06:56 +0300 Subject: [PATCH 93/96] remove unneeded files --- roles/promtail/.ansible-lint | 16 ---------------- roles/promtail/.gitignore | 2 -- roles/promtail/.yamllint | 8 -------- roles/promtail/renovate.json | 6 ------ roles/promtail/requirements.ci.txt | 5 ----- 5 files changed, 37 deletions(-) delete mode 100644 roles/promtail/.ansible-lint delete mode 100644 roles/promtail/.gitignore delete mode 100644 roles/promtail/.yamllint delete mode 100644 roles/promtail/renovate.json delete mode 100644 roles/promtail/requirements.ci.txt diff --git a/roles/promtail/.ansible-lint b/roles/promtail/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/promtail/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/promtail/.gitignore b/roles/promtail/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/promtail/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/promtail/.yamllint b/roles/promtail/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/promtail/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/promtail/renovate.json b/roles/promtail/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/promtail/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/promtail/requirements.ci.txt b/roles/promtail/requirements.ci.txt deleted file mode 100644 index 5eabc75..0000000 --- a/roles/promtail/requirements.ci.txt +++ /dev/null @@ -1,5 +0,0 @@ -ansible -molecule -molecule-plugins[docker] -docker -requests==2.31.0 # pinned to the latest version not breaking Docker SDK \ No newline at end of file From 471be205638537a7b8460a6aa9f19d758bcc2351 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:15:58 +0300 Subject: [PATCH 94/96] fix kics error --- roles/promtail/molecule/default/verify.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/promtail/molecule/default/verify.yml b/roles/promtail/molecule/default/verify.yml index e1e0984..7e078c2 100644 --- a/roles/promtail/molecule/default/verify.yml +++ b/roles/promtail/molecule/default/verify.yml @@ -9,6 +9,7 @@ dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" extensions: ['yml'] + # kics-scan ignore-block - kics doesn't like http but it's localhost so it's not important - name: "Sanity check the Promtail daemon" retries: 3 delay: 1 From cadbc7398619e04427a9faa2886d769f5b71d752 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:21:17 +0300 Subject: [PATCH 95/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f383011..3aed186 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) +- [promtail](roles/promtail/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) - [rustdesk](roles/rustdesk/README.md) - [sftp_share](roles/sftp_share/README.md) diff --git a/galaxy.yml b/galaxy.yml index a760250..a47d750 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.18.0 +version: 0.19.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 38796c1a0ddf6db3fab812ed368d2ed8b6784b64 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:25:25 +0300 Subject: [PATCH 96/96] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3aed186..7217749 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [loki](roles/loki/README.md) - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) +- [node_exporter](roles/node_exporter/README.md) - [prometheus](roles/prometheus/README.md) - [promtail](roles/promtail/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) diff --git a/galaxy.yml b/galaxy.yml index a47d750..e8ba3ce 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.19.0 +version: 0.20.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me)