From 48803604cd3f8640ed8dd36357044a3fd51b6490 Mon Sep 17 00:00:00 2001 From: Sam Doooran Date: Thu, 17 Dec 2020 15:29:22 -0500 Subject: [PATCH] systemd - do not overwrite unit name when searching (#72985) * systemd - do not overwrite unit name when searching PR #72702 introduced a bug that changed the unit name when splitting it up for the purpose of searching for the unit. This only happens on unit file templates on systems that have a 5.8 or newer kernel and a version of systemd that does not contain a bugfix that causes systmed to fail to parse dbus. * Use facts rather than a manual probe to determine if systmed is present * Remov unnecessary block * Use vars files instead of set_fact * Add tests for using a templated unit file * Update changelog fragment * Use template to get correct path to sleep binary --- .../systemd-preserve-full-unit-name.yml | 4 + lib/ansible/modules/systemd.py | 8 +- .../targets/systemd/handlers/main.yml | 4 + .../targets/systemd/tasks/main.yml | 133 +++++++++--------- .../systemd/tasks/test_unit_template.yml | 50 +++++++ .../systemd/templates/sleeper@.service | 8 ++ .../targets/systemd/vars/Debian.yml | 2 + .../targets/systemd/vars/default.yml | 2 + 8 files changed, 143 insertions(+), 68 deletions(-) create mode 100644 changelogs/fragments/systemd-preserve-full-unit-name.yml create mode 100644 test/integration/targets/systemd/handlers/main.yml create mode 100644 test/integration/targets/systemd/tasks/test_unit_template.yml create mode 100644 test/integration/targets/systemd/templates/sleeper@.service create mode 100644 test/integration/targets/systemd/vars/Debian.yml create mode 100644 test/integration/targets/systemd/vars/default.yml diff --git a/changelogs/fragments/systemd-preserve-full-unit-name.yml b/changelogs/fragments/systemd-preserve-full-unit-name.yml new file mode 100644 index 00000000000..1af70a358d5 --- /dev/null +++ b/changelogs/fragments/systemd-preserve-full-unit-name.yml @@ -0,0 +1,4 @@ +bugfixes: + - > + systemd - preserve the full unit name when using a templated service and + ``systemd`` failed to parse dbus due to a known bug in ``systemd`` (https://github.com/ansible/ansible/pull/72985) diff --git a/lib/ansible/modules/systemd.py b/lib/ansible/modules/systemd.py index c9c226464ab..1c2485abf22 100644 --- a/lib/ansible/modules/systemd.py +++ b/lib/ansible/modules/systemd.py @@ -415,10 +415,10 @@ def main(): elif err and rc == 1 and 'Failed to parse bus message' in err: result['status'] = parse_systemctl_show(to_native(out).split('\n')) - unit, sep, suffix = unit.partition('@') - unit_search = '{unit}{sep}*'.format(unit=unit, sep=sep) - (rc, out, err) = module.run_command("{systemctl} list-unit-files '{unit_search}'".format(systemctl=systemctl, unit_search=unit_search)) - is_systemd = unit in out + unit_base, sep, suffix = unit.partition('@') + unit_search = '{unit_base}{sep}'.format(unit_base=unit_base, sep=sep) + (rc, out, err) = module.run_command("{systemctl} list-unit-files '{unit_search}*'".format(systemctl=systemctl, unit_search=unit_search)) + is_systemd = unit_search in out (rc, out, err) = module.run_command("{systemctl} is-active '{unit}'".format(systemctl=systemctl, unit=unit)) result['status']['ActiveState'] = out.rstrip('\n') diff --git a/test/integration/targets/systemd/handlers/main.yml b/test/integration/targets/systemd/handlers/main.yml new file mode 100644 index 00000000000..8643a2a0eeb --- /dev/null +++ b/test/integration/targets/systemd/handlers/main.yml @@ -0,0 +1,4 @@ +- name: remove unit file + file: + path: /etc/systemd/system/sleeper@.service + state: absent diff --git a/test/integration/targets/systemd/tasks/main.yml b/test/integration/targets/systemd/tasks/main.yml index 867a554deae..96781eb8255 100644 --- a/test/integration/targets/systemd/tasks/main.yml +++ b/test/integration/targets/systemd/tasks/main.yml @@ -20,70 +20,73 @@ ## systemctl ## -- name: check for systemctl command - shell: which systemctl - failed_when: False - register: systemctl_check - -- meta: end_host - when: systemctl_check.rc != 0 - -- set_fact: - ssh_service: '{{ "ssh" if ansible_os_family == "Debian" else "sshd" }}' - -- block: - - name: get a list of running services - shell: systemctl | fgrep 'running' | awk '{print $1}' | sed 's/\.service//g' | fgrep -v '.' | egrep ^[a-z] - register: running_names - - debug: var=running_names - - - name: check running state - systemd: - name: "{{ running_names.stdout_lines|random }}" - state: started - register: systemd_test0 - - debug: var=systemd_test0 - - name: validate results for test0 - assert: - that: - - 'systemd_test0.changed is defined' - - 'systemd_test0.name is defined' - - 'systemd_test0.state is defined' - - 'systemd_test0.status is defined' - - 'not systemd_test0.changed' - - 'systemd_test0.state == "started"' - - - name: the module must fail when a service is not found - systemd: - name: '{{ fake_service }}' - state: stopped - register: result - ignore_errors: yes - - - assert: - that: - - result is failed - - 'result is search("Could not find the requested service {{ fake_service }}")' - - - name: the module must fail in check_mode as well when a service is not found - systemd: - name: '{{ fake_service }}' - state: stopped - register: result - check_mode: yes - ignore_errors: yes - - - assert: - that: - - result is failed - - 'result is search("Could not find the requested service {{ fake_service }}")' - - - name: check that the module works even when systemd is offline (eg in chroot) - systemd: - name: "{{ running_names.stdout_lines|random }}" - state: started - environment: - SYSTEMD_OFFLINE: 1 +- name: End if this system does not use systemd + meta: end_host + when: ansible_facts.service_mgr != 'systemd' + +- name: Include distribution specific variables + include_vars: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - "{{ ansible_facts.distribution }}.yml" + - "{{ ansible_facts.os_family }}.yml" + - default.yml + paths: + - vars + +- name: get a list of running services + shell: systemctl | fgrep 'running' | awk '{print $1}' | sed 's/\.service//g' | fgrep -v '.' | egrep ^[a-z] + register: running_names +- debug: var=running_names + +- name: check running state + systemd: + name: "{{ running_names.stdout_lines|random }}" + state: started + register: systemd_test0 +- debug: var=systemd_test0 +- name: validate results for test0 + assert: + that: + - 'systemd_test0.changed is defined' + - 'systemd_test0.name is defined' + - 'systemd_test0.state is defined' + - 'systemd_test0.status is defined' + - 'not systemd_test0.changed' + - 'systemd_test0.state == "started"' + +- name: the module must fail when a service is not found + systemd: + name: '{{ fake_service }}' + state: stopped + register: result + ignore_errors: yes + +- assert: + that: + - result is failed + - 'result is search("Could not find the requested service {{ fake_service }}")' + +- name: the module must fail in check_mode as well when a service is not found + systemd: + name: '{{ fake_service }}' + state: stopped + register: result + check_mode: yes + ignore_errors: yes + +- assert: + that: + - result is failed + - 'result is search("Could not find the requested service {{ fake_service }}")' + +- name: check that the module works even when systemd is offline (eg in chroot) + systemd: + name: "{{ running_names.stdout_lines|random }}" + state: started + environment: + SYSTEMD_OFFLINE: 1 - name: Disable ssh 1 systemd: @@ -114,3 +117,5 @@ - systemd_disable_ssh_2 is not changed - systemd_enable_ssh_1 is changed - systemd_enable_ssh_2 is not changed + +- import_tasks: test_unit_template.yml diff --git a/test/integration/targets/systemd/tasks/test_unit_template.yml b/test/integration/targets/systemd/tasks/test_unit_template.yml new file mode 100644 index 00000000000..47cb1c78729 --- /dev/null +++ b/test/integration/targets/systemd/tasks/test_unit_template.yml @@ -0,0 +1,50 @@ +- name: Copy service file + template: + src: sleeper@.service + dest: /etc/systemd/system/sleeper@.service + owner: root + group: root + mode: '0644' + notify: remove unit file + +- name: Reload systemd + systemd: + daemon_reload: yes + +- name: Start and enable service using unit template + systemd: + name: sleeper@100.service + state: started + enabled: yes + register: template_test_1 + +- name: Start and enable service using unit template again + systemd: + name: sleeper@100.service + state: started + enabled: yes + register: template_test_2 + +- name: Stop and disable service using unit template + systemd: + name: sleeper@100.service + state: stopped + enabled: no + register: template_test_3 + +- name: Stop and disable service using unit template again + systemd: + name: sleeper@100.service + state: stopped + enabled: no + register: template_test_4 + +- name: + assert: + that: + - template_test_1 is changed + - template_test_1 is success + - template_test_2 is not changed + - template_test_2 is success + - template_test_3 is changed + - template_test_4 is not changed diff --git a/test/integration/targets/systemd/templates/sleeper@.service b/test/integration/targets/systemd/templates/sleeper@.service new file mode 100644 index 00000000000..8b47982a013 --- /dev/null +++ b/test/integration/targets/systemd/templates/sleeper@.service @@ -0,0 +1,8 @@ +[Unit] +Description=Basic service to use as a template + +[Service] +ExecStart={{ sleep_bin_path }} %i + +[Install] +WantedBy=multi-user.target diff --git a/test/integration/targets/systemd/vars/Debian.yml b/test/integration/targets/systemd/vars/Debian.yml new file mode 100644 index 00000000000..9760744d07d --- /dev/null +++ b/test/integration/targets/systemd/vars/Debian.yml @@ -0,0 +1,2 @@ +ssh_service: ssh +sleep_bin_path: /bin/sleep diff --git a/test/integration/targets/systemd/vars/default.yml b/test/integration/targets/systemd/vars/default.yml new file mode 100644 index 00000000000..57491ff0df7 --- /dev/null +++ b/test/integration/targets/systemd/vars/default.yml @@ -0,0 +1,2 @@ +ssh_service: sshd +sleep_bin_path: /usr/bin/sleep