mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-27 10:40:22 -07:00
launchd: new module to control services on macOS hosts (#305)
This commit is contained in:
parent
669b7bf090
commit
80cd8329e0
21 changed files with 947 additions and 0 deletions
3
tests/integration/targets/launchd/aliases
Normal file
3
tests/integration/targets/launchd/aliases
Normal file
|
@ -0,0 +1,3 @@
|
|||
shippable/posix/group1
|
||||
skip/freebsd
|
||||
skip/rhel
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.version_info[0] >= 3:
|
||||
import http.server
|
||||
import socketserver
|
||||
PORT = int(sys.argv[1])
|
||||
Handler = http.server.SimpleHTTPRequestHandler
|
||||
httpd = socketserver.TCPServer(("", PORT), Handler)
|
||||
httpd.serve_forever()
|
||||
else:
|
||||
import mimetypes
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/json', '.json')
|
||||
import SimpleHTTPServer
|
||||
SimpleHTTPServer.test()
|
4
tests/integration/targets/launchd/meta/main.yml
Normal file
4
tests/integration/targets/launchd/meta/main.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
|
||||
dependencies:
|
||||
- prepare_tests
|
22
tests/integration/targets/launchd/tasks/main.yml
Normal file
22
tests/integration/targets/launchd/tasks/main.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
|
||||
- name: Test launchd module
|
||||
block:
|
||||
- name: Expect that launchctl exists
|
||||
stat:
|
||||
path: /bin/launchctl
|
||||
register: launchctl_check
|
||||
failed_when:
|
||||
- not launchctl_check.stat.exists
|
||||
|
||||
- name: Run tests
|
||||
include_tasks: test.yml
|
||||
with_items:
|
||||
- test_unknown
|
||||
- test_start_stop
|
||||
- test_restart
|
||||
- test_unload
|
||||
- test_reload
|
||||
- test_runatload
|
||||
|
||||
when: ansible_os_family == 'Darwin'
|
20
tests/integration/targets/launchd/tasks/setup.yml
Normal file
20
tests/integration/targets/launchd/tasks/setup.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
|
||||
- name: "[{{ item }}] Deploy test service configuration"
|
||||
template:
|
||||
src: "{{ launchd_service_name }}.plist.j2"
|
||||
dest: "{{ launchd_plist_location }}"
|
||||
become: yes
|
||||
|
||||
- name: install the test daemon script
|
||||
copy:
|
||||
src: ansible_test_service.py
|
||||
dest: /usr/local/sbin/ansible_test_service
|
||||
mode: '755'
|
||||
|
||||
- name: rewrite shebang in the test daemon script
|
||||
lineinfile:
|
||||
path: /usr/local/sbin/ansible_test_service
|
||||
line: "#!{{ ansible_python_interpreter | realpath }}"
|
||||
insertbefore: BOF
|
||||
firstmatch: yes
|
27
tests/integration/targets/launchd/tasks/teardown.yml
Normal file
27
tests/integration/targets/launchd/tasks/teardown.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
|
||||
- name: "[{{ item }}] Unload service"
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: unloaded
|
||||
become: yes
|
||||
register: launchd_unloaded_result
|
||||
|
||||
- name: "[{{ item }}] Validation"
|
||||
assert:
|
||||
that:
|
||||
- launchd_unloaded_result is success
|
||||
- launchd_unloaded_result.status.current_state == 'unloaded'
|
||||
- launchd_unloaded_result.status.current_pid == '-'
|
||||
|
||||
- name: "[{{ item }}] Remove test service configuration"
|
||||
file:
|
||||
path: "{{ launchd_plist_location }}"
|
||||
state: absent
|
||||
become: yes
|
||||
|
||||
- name: "[{{ item }}] Remove test service server"
|
||||
file:
|
||||
path: "/usr/local/sbin/ansible_test_service"
|
||||
state: absent
|
||||
become: yes
|
8
tests/integration/targets/launchd/tasks/test.yml
Normal file
8
tests/integration/targets/launchd/tasks/test.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
|
||||
- name: "Running {{ item }}"
|
||||
block:
|
||||
- include_tasks: setup.yml
|
||||
- include_tasks: "tests/{{ item }}.yml"
|
||||
always:
|
||||
- include_tasks: teardown.yml
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a started service in check_mode"
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result_check_mode"
|
||||
check_mode: yes
|
||||
|
||||
- name: "[{{ item }}] Assert that everything work in check mode"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_start_result_check_mode is success
|
||||
- test_1_launchd_start_result_check_mode is changed
|
||||
|
||||
- name: "[{{ item }}] Given a started service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result"
|
||||
|
||||
|
||||
- name: "[{{ item }}] The started service should run on port 21212"
|
||||
wait_for:
|
||||
port: 21212
|
||||
delay: 5
|
||||
timeout: 10
|
||||
|
||||
- name: "[{{ item }}] Deploy a new test service configuration with a new port 21213"
|
||||
template:
|
||||
src: "modified.{{ launchd_service_name }}.plist.j2"
|
||||
dest: "{{ launchd_plist_location }}"
|
||||
become: yes
|
||||
|
||||
- name: "[{{ item }}] When reloading the service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: reloaded
|
||||
become: yes
|
||||
register: "test_1_launchd_reload_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service was reloaded"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_reload_result is success
|
||||
- test_1_launchd_reload_result is changed
|
||||
- test_1_launchd_reload_result.status.previous_pid == test_1_launchd_start_result.status.current_pid
|
||||
- test_1_launchd_reload_result.status.previous_state == test_1_launchd_start_result.status.current_state
|
||||
- test_1_launchd_reload_result.status.current_state == 'stopped'
|
||||
- test_1_launchd_reload_result.status.current_pid == '-'
|
||||
|
||||
- name: "[{{ item }}] Start the service with the new configuration..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result"
|
||||
|
||||
|
||||
- name: "[{{ item }}] The started service should run on port 21213"
|
||||
wait_for:
|
||||
port: 21213
|
||||
delay: 5
|
||||
timeout: 10
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a started service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result"
|
||||
|
||||
|
||||
- name: "[{{ item }}] When restarting the service in check mode"
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: restarted
|
||||
become: yes
|
||||
register: "test_1_launchd_restart_result_check_mode"
|
||||
check_mode: yes
|
||||
|
||||
- name: "[{{ item }}] Validate that service was restarted in check mode"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_restart_result_check_mode is success
|
||||
- test_1_launchd_restart_result_check_mode is changed
|
||||
|
||||
- name: "[{{ item }}] When restarting the service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: restarted
|
||||
become: yes
|
||||
register: "test_1_launchd_restart_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service was restarted"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_restart_result is success
|
||||
- test_1_launchd_restart_result is changed
|
||||
- test_1_launchd_restart_result.status.previous_pid == test_1_launchd_start_result.status.current_pid
|
||||
- test_1_launchd_restart_result.status.previous_state == test_1_launchd_start_result.status.current_state
|
||||
- test_1_launchd_restart_result.status.current_state == 'started'
|
||||
- test_1_launchd_restart_result.status.current_pid != '-'
|
||||
- test_1_launchd_restart_result.status.status_code == '0'
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a started service with RunAtLoad set to true..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
enabled: yes
|
||||
become: yes
|
||||
register: test_1_launchd_start_result
|
||||
|
||||
- name: "[{{ item }}] Validate that service was started"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_start_result is success
|
||||
- test_1_launchd_start_result is changed
|
||||
- test_1_launchd_start_result.status.previous_pid == '-'
|
||||
- test_1_launchd_start_result.status.previous_state == 'unloaded'
|
||||
- test_1_launchd_start_result.status.current_state == 'started'
|
||||
- test_1_launchd_start_result.status.current_pid != '-'
|
||||
- test_1_launchd_start_result.status.status_code == '0'
|
||||
|
||||
- name: "[{{ item }}] Validate that RunAtLoad is set to true"
|
||||
replace:
|
||||
path: "{{ launchd_plist_location }}"
|
||||
regexp: |
|
||||
\s+<key>RunAtLoad</key>
|
||||
\s+<true/>
|
||||
replace: found_run_at_load
|
||||
check_mode: yes
|
||||
register: contents_would_have
|
||||
failed_when: not contents_would_have is changed
|
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a started service in check mode"
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result_check_mode"
|
||||
check_mode: yes
|
||||
|
||||
|
||||
- name: "[{{ item }}] Validate that service was started in check mode"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_start_result_check_mode is success
|
||||
- test_1_launchd_start_result_check_mode is changed
|
||||
|
||||
|
||||
- name: "[{{ item }}] Given a started service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result"
|
||||
|
||||
|
||||
- name: "[{{ item }}] Validate that service was started"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_start_result is success
|
||||
- test_1_launchd_start_result is changed
|
||||
- test_1_launchd_start_result.status.previous_pid == '-'
|
||||
- test_1_launchd_start_result.status.previous_state == 'unloaded'
|
||||
- test_1_launchd_start_result.status.current_state == 'started'
|
||||
- test_1_launchd_start_result.status.current_pid != '-'
|
||||
- test_1_launchd_start_result.status.status_code == '0'
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a stopped service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: stopped
|
||||
become: yes
|
||||
register: "test_2_launchd_stop_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service was stopped after it was started"
|
||||
assert:
|
||||
that:
|
||||
- test_2_launchd_stop_result is success
|
||||
- test_2_launchd_stop_result is changed
|
||||
- test_2_launchd_stop_result.status.previous_pid == test_1_launchd_start_result.status.current_pid
|
||||
- test_2_launchd_stop_result.status.previous_state == test_1_launchd_start_result.status.current_state
|
||||
- test_2_launchd_stop_result.status.current_state == 'stopped'
|
||||
- test_2_launchd_stop_result.status.current_pid == '-'
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a stopped service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: stopped
|
||||
become: yes
|
||||
register: "test_3_launchd_stop_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service can be stopped after being already stopped"
|
||||
assert:
|
||||
that:
|
||||
- test_3_launchd_stop_result is success
|
||||
- not test_3_launchd_stop_result is changed
|
||||
- test_3_launchd_stop_result.status.previous_pid == '-'
|
||||
- test_3_launchd_stop_result.status.previous_state == 'stopped'
|
||||
- test_3_launchd_stop_result.status.current_state == 'stopped'
|
||||
- test_3_launchd_stop_result.status.current_pid == '-'
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a started service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_4_launchd_start_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service was started..."
|
||||
assert:
|
||||
that:
|
||||
- test_4_launchd_start_result is success
|
||||
- test_4_launchd_start_result is changed
|
||||
- test_4_launchd_start_result.status.previous_pid == '-'
|
||||
- test_4_launchd_start_result.status.previous_state == 'stopped'
|
||||
- test_4_launchd_start_result.status.current_state == 'started'
|
||||
- test_4_launchd_start_result.status.current_pid != '-'
|
||||
- test_4_launchd_start_result.status.status_code == '0'
|
||||
|
||||
- name: "[{{ item }}] And when service is started again..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_5_launchd_start_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service is still in the same state as before"
|
||||
assert:
|
||||
that:
|
||||
- test_5_launchd_start_result is success
|
||||
- not test_5_launchd_start_result is changed
|
||||
- test_5_launchd_start_result.status.previous_pid == test_4_launchd_start_result.status.current_pid
|
||||
- test_5_launchd_start_result.status.previous_state == test_4_launchd_start_result.status.current_state
|
||||
- test_5_launchd_start_result.status.status_code == '0'
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Expect that an error occurs when an unknown service is used."
|
||||
launchd:
|
||||
name: com.acme.unknownservice
|
||||
state: started
|
||||
register: result
|
||||
failed_when:
|
||||
- not '"Unable to infer the path of com.acme.unknownservice service plist file and it was not found among active services" in result.msg'
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given a started service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: started
|
||||
become: yes
|
||||
register: "test_1_launchd_start_result"
|
||||
|
||||
|
||||
- name: "[{{ item }}] When unloading the service in check mode"
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: unloaded
|
||||
become: yes
|
||||
register: "test_1_launchd_unloaded_result_check_mode"
|
||||
check_mode: yes
|
||||
|
||||
- name: "[{{ item }}] Validate that service was unloaded in check mode"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_unloaded_result_check_mode is success
|
||||
- test_1_launchd_unloaded_result_check_mode is changed
|
||||
|
||||
|
||||
- name: "[{{ item }}] When unloading the service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: unloaded
|
||||
become: yes
|
||||
register: "test_1_launchd_unloaded_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service was unloaded"
|
||||
assert:
|
||||
that:
|
||||
- test_1_launchd_unloaded_result is success
|
||||
- test_1_launchd_unloaded_result is changed
|
||||
- test_1_launchd_unloaded_result.status.previous_pid == test_1_launchd_start_result.status.current_pid
|
||||
- test_1_launchd_unloaded_result.status.previous_state == test_1_launchd_start_result.status.current_state
|
||||
- test_1_launchd_unloaded_result.status.current_state == 'unloaded'
|
||||
- test_1_launchd_unloaded_result.status.current_pid == '-'
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
- name: "[{{ item }}] Given an unloaded service on an unloaded service..."
|
||||
launchd:
|
||||
name: "{{ launchd_service_name }}"
|
||||
state: unloaded
|
||||
become: yes
|
||||
register: "test_2_launchd_unloaded_result"
|
||||
|
||||
- name: "[{{ item }}] Validate that service did not change and is still unloaded"
|
||||
assert:
|
||||
that:
|
||||
- test_2_launchd_unloaded_result is success
|
||||
- not test_2_launchd_unloaded_result is changed
|
||||
- test_2_launchd_unloaded_result.status.previous_pid == '-'
|
||||
- test_2_launchd_unloaded_result.status.previous_state == 'unloaded'
|
||||
- test_2_launchd_unloaded_result.status.current_state == 'unloaded'
|
||||
- test_2_launchd_unloaded_result.status.current_pid == '-'
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>{{ launchd_service_name }}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/sbin/ansible_test_service</string>
|
||||
<string>21212</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>{{ launchd_service_name }}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/sbin/ansible_test_service</string>
|
||||
<string>21213</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
4
tests/integration/targets/launchd/vars/main.yml
Normal file
4
tests/integration/targets/launchd/vars/main.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
|
||||
launchd_service_name: launchd.test.service
|
||||
launchd_plist_location: /Library/LaunchDaemons/{{ launchd_service_name }}.plist
|
Loading…
Add table
Add a link
Reference in a new issue