Refactor systemctl service handling with @ support

- Unified variable naming: system_service_id → systemctl_id
- Added automatic removal of trailing '@' for role directory resolution
- Improved first_found search: prefer target role, fallback to sys-systemctl defaults
- Split template resolution logic to avoid undefined variable errors
- Added assertion in sys-timer to forbid '@' in systemctl_id
- Corrected default systemctl.service.j2 template description
- Cleaned up path handling and script directory generation

Context: conversation about fixing template resolution and @ handling
https://chatgpt.com/share/68a39994-1bb0-800f-a219-109e643c3efb
This commit is contained in:
Kevin Veen-Birkenbach 2025-08-18 23:22:46 +02:00
parent b9461026a6
commit 185f37af52
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
9 changed files with 76 additions and 44 deletions

View File

@ -1,18 +1,18 @@
# filter_plugins/get_service_script_path.py # filter_plugins/get_service_script_path.py
# Custom Ansible filter to generate service script paths. # Custom Ansible filter to generate service script paths.
def get_service_script_path(system_service_id, script_type): def get_service_script_path(systemctl_id, script_type):
""" """
Build the path to a service script based on system_service_id and type. Build the path to a service script based on systemctl_id and type.
:param system_service_id: The identifier of the system service. :param systemctl_id: The identifier of the system service.
:param script_type: The script type/extension (e.g., sh, py, yml). :param script_type: The script type/extension (e.g., sh, py, yml).
:return: The full path string. :return: The full path string.
""" """
if not system_service_id or not script_type: if not systemctl_id or not script_type:
raise ValueError("Both system_service_id and script_type are required") raise ValueError("Both systemctl_id and script_type are required")
return f"/opt/scripts/{system_service_id}/script.{script_type}" return f"/opt/scripts/{systemctl_id}/script.{script_type}"
class FilterModule(object): class FilterModule(object):

View File

@ -1 +1 @@
systemctl_id: sys-ctl-alm-compose systemctl_id: sys-ctl-alm-compose@

View File

@ -1 +1 @@
systemctl_id: sys-ctl-alm-email systemctl_id: sys-ctl-alm-email@

View File

@ -1,2 +1 @@
systemctl_id: sys-ctl-alm-telegram systemctl_id: sys-ctl-alm-telegram@

View File

@ -1,18 +1,20 @@
# roles/sys-systemctl/tasks/03_base.yml
- name: "find best matching source for service script" - name: "find best matching source for service script"
set_fact: set_fact:
service_src: >- service_src: >-
{{ {{ lookup('first_found',
lookup('first_found', { {
'files': [ 'files': [
'templates/script.sh.j2', 'templates/script.sh.j2',
'templates/script.py.j2', 'templates/script.py.j2',
'files/script.sh', 'files/script.sh',
'files/script.py' 'files/script.py'
] ],
}, errors='strict') 'paths': [ systemctl_role_dir ]
}} },
when: errors='strict'
- systemctl_copy_files | bool ) }}
when: systemctl_copy_files | bool
- name: "Load file logic for '{{ systemctl_id }}'" - name: "Load file logic for '{{ systemctl_id }}'"
include_tasks: 04_files.yml include_tasks: 04_files.yml

View File

@ -1,19 +1,38 @@
- name: "setup systemctl {{ item }} '{{ systemctl_id }}'" # 1) Find the template (prefer target role, then fall back to this role)
- name: Resolve systemctl template source
set_fact:
systemctl_template_src: >-
{{ lookup(
'first_found',
{
'files': [
'templates/systemctl@.service.j2',
'templates/systemctl.service.j2'
],
'paths': [
systemctl_role_dir,
role_path
]
},
errors='strict'
) }}
# Optional: sanity check with a clear error if truly nothing found
- name: Ensure a systemctl template was found
assert:
that: systemctl_template_src | length > 0
fail_msg: >-
Could not resolve any systemctl template. Looked in:
{{ systemctl_role_dir }}/templates/ and {{ role_path }}/templates/.
# 2) Now we may safely derive whether its the “@” variant
- name: Flag whether @-template is used
set_fact:
systemctl_uses_at: "{{ (systemctl_template_src | basename) is search('@\\.service\\.j2$') }}"
# 3) Use it
- name: "setup systemctl '{{ systemctl_id }}'"
template: template:
src: "{{ lookup( src: "{{ systemctl_template_src }}"
'first_found', dest: "{{ [ PATH_SYSTEM_SERVICE_DIR, systemctl_id | get_service_name(SOFTWARE_NAME) ] | path_join }}"
{ notify: "{{ 'reload system daemon' if systemctl_uses_at else 'refresh systemctl service' }}"
'files': ['templates/systemctl' ~ item ~ '.service.j2'],
'paths': [systemctl_role_dir, role_path]
},
errors='strict'
) }}"
dest: "{{ [ PATH_SYSTEM_SERVICE_DIR, systemctl_id ~ item ~ SYS_SERVICE_SUFFIX ] | path_join }}"
notify: "{{ 'reload system daemon' if item == '@' else 'refresh systemctl service' }}"
register: services_template
failed_when:
- services_template is failed
- "'Could not find or access' not in services_template.msg"
loop:
- ""
- "@"

View File

@ -1,4 +1,11 @@
- set_fact: - name: Fail if systemctl_id contains "@"
assert:
that:
- "'@' not in systemctl_id"
fail_msg: "Invalid systemctl_id '{{ systemctl_id }}' → must not contain '@'."
- name: "Make '{{ systemctl_id }}' available for sys-timer"
set_fact:
systemctl_timer_service: "{{ systemctl_id }}" systemctl_timer_service: "{{ systemctl_id }}"
- name: "include role for sys-timer for {{ systemctl_timer_service }}" - name: "include role for sys-timer for {{ systemctl_timer_service }}"

View File

@ -1,5 +1,5 @@
[Unit] [Unit]
Description=Service for {{ SOFTWARE_NAME }} role 'systemctl_id' (DEFAULT TEMPLATE) Description=Service for {{ SOFTWARE_NAME }} role '{{ systemctl_id }}' (DEFAULT TEMPLATE)
OnFailure={{ SYS_SERVICE_ON_FAILURE_COMPOSE }} OnFailure={{ SYS_SERVICE_ON_FAILURE_COMPOSE }}
[Service] [Service]

View File

@ -1,6 +1,11 @@
UNIT_SUFFIX_REMOVER_PACKAGE: "unsure" UNIT_SUFFIX_REMOVER_PACKAGE: "unsure"
systemctl_script_dir: "{{ [ PATH_SYSTEMCTL_SCRIPTS, systemctl_id ] | path_join }}"
systemctl_role_dir: "{{ playbook_dir }}/roles/{{ systemctl_id }}" ## Paths
systemctl_copy_files: true # When set to false file copying will be skipped systemctl_role_name: "{{ systemctl_id | regex_replace('@','') }}"
systemctl_timer_enabled: false # When set to true timmer will be loaded systemctl_role_dir: "{{ [ playbook_dir, 'roles', systemctl_role_name ] | path_join }}"
systemctl_state: "{{ omit }}" systemctl_script_dir: "{{ [ PATH_SYSTEMCTL_SCRIPTS, systemctl_id ] | path_join }}"
## Settings
systemctl_copy_files: true # When set to false file copying will be skipped
systemctl_timer_enabled: false # When set to true timmer will be loaded
systemctl_state: "{{ omit }}"