mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-07 12:37:17 +00:00
add integration tests + fixes
This commit is contained in:
parent
00152dbb63
commit
816f31c19f
13 changed files with 393 additions and 25 deletions
1
tests/integration/targets/monit/aliases
Normal file
1
tests/integration/targets/monit/aliases
Normal file
|
|
@ -0,0 +1 @@
|
|||
destructive
|
||||
124
tests/integration/targets/monit/files/daemon.py
Normal file
124
tests/integration/targets/monit/files/daemon.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"""Generic linux daemon base class for python.
|
||||
http://www.jejik.com/files/examples/daemon3x.py
|
||||
"""
|
||||
|
||||
import sys, os, time, atexit, signal
|
||||
|
||||
|
||||
class Daemon:
|
||||
"""A generic daemon class.
|
||||
|
||||
Usage: subclass the daemon class and override the run() method."""
|
||||
|
||||
def __init__(self, pidfile):
|
||||
self.pidfile = pidfile
|
||||
|
||||
def daemonize(self):
|
||||
"""Deamonize class. UNIX double fork mechanism."""
|
||||
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit first parent
|
||||
sys.exit(0)
|
||||
except OSError as err:
|
||||
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
|
||||
sys.exit(1)
|
||||
|
||||
# decouple from parent environment
|
||||
os.chdir('/')
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
# do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit from second parent
|
||||
sys.exit(0)
|
||||
except OSError as err:
|
||||
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
|
||||
sys.exit(1)
|
||||
|
||||
# redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
si = open(os.devnull, 'r')
|
||||
so = open(os.devnull, 'a+')
|
||||
se = open(os.devnull, 'a+')
|
||||
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
# write pidfile
|
||||
atexit.register(self.delpid)
|
||||
|
||||
pid = str(os.getpid())
|
||||
with open(self.pidfile, 'w+') as f:
|
||||
f.write(pid + '\n')
|
||||
|
||||
def delpid(self):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
def start(self):
|
||||
"""Start the daemon."""
|
||||
|
||||
# Check for a pidfile to see if the daemon already runs
|
||||
try:
|
||||
with open(self.pidfile, 'r') as pf:
|
||||
|
||||
pid = int(pf.read().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if pid:
|
||||
message = "pidfile {0} already exist. " + \
|
||||
"Daemon already running?\n"
|
||||
sys.stderr.write(message.format(self.pidfile))
|
||||
sys.exit(1)
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
self.run()
|
||||
|
||||
def stop(self):
|
||||
"""Stop the daemon."""
|
||||
|
||||
# Get the pid from the pidfile
|
||||
try:
|
||||
with open(self.pidfile, 'r') as pf:
|
||||
pid = int(pf.read().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if not pid:
|
||||
message = "pidfile {0} does not exist. " + \
|
||||
"Daemon not running?\n"
|
||||
sys.stderr.write(message.format(self.pidfile))
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
while 1:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.1)
|
||||
except OSError as err:
|
||||
e = str(err.args)
|
||||
if e.find("No such process") > 0:
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
else:
|
||||
print(str(err.args))
|
||||
sys.exit(1)
|
||||
|
||||
def restart(self):
|
||||
"""Restart the daemon."""
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
"""You should override this method when you subclass Daemon.
|
||||
|
||||
It will be called after the process has been daemonized by
|
||||
start() or restart()."""
|
||||
57
tests/integration/targets/monit/files/httpd_echo.py
Normal file
57
tests/integration/targets/monit/files/httpd_echo.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import sys
|
||||
from daemon import Daemon
|
||||
|
||||
try:
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
def write_to_output(stream, content):
|
||||
stream.write(content)
|
||||
except ImportError:
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
def write_to_output(stream, content):
|
||||
stream.write(bytes(content, "utf-8"))
|
||||
|
||||
|
||||
hostname = "localhost"
|
||||
server_port = 8082
|
||||
|
||||
|
||||
class MyServer(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
write_to_output(self.wfile, self.path)
|
||||
|
||||
|
||||
class MyDaemon(Daemon):
|
||||
def run(self):
|
||||
webServer = HTTPServer((hostname, server_port), MyServer)
|
||||
print("Server started http://%s:%s" % (hostname, server_port))
|
||||
|
||||
try:
|
||||
webServer.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
webServer.server_close()
|
||||
print("Server stopped.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
daemon = MyDaemon('/tmp/httpd_echo.pid')
|
||||
if len(sys.argv) == 2:
|
||||
if 'start' == sys.argv[1]:
|
||||
daemon.start()
|
||||
elif 'stop' == sys.argv[1]:
|
||||
daemon.stop()
|
||||
elif 'restart' == sys.argv[1]:
|
||||
daemon.restart()
|
||||
else:
|
||||
print("Unknown command")
|
||||
sys.exit(2)
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("usage: %s start|stop|restart" % sys.argv[0])
|
||||
sys.exit(2)
|
||||
16
tests/integration/targets/monit/handlers/main.yml
Normal file
16
tests/integration/targets/monit/handlers/main.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
- name: start monit
|
||||
become: yes
|
||||
service: name=monit state=started
|
||||
|
||||
- name: restart monit
|
||||
become: yes
|
||||
service: name=monit state=restarted
|
||||
|
||||
- name: reload monit
|
||||
become: yes
|
||||
service: name=monit state=reloaded
|
||||
|
||||
- name: stop monit
|
||||
become: yes
|
||||
service: name=monit state=stopped
|
||||
2
tests/integration/targets/monit/meta/main.yml
Normal file
2
tests/integration/targets/monit/meta/main.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
dependencies:
|
||||
- setup_pkg_mgr
|
||||
53
tests/integration/targets/monit/tasks/main.yml
Normal file
53
tests/integration/targets/monit/tasks/main.yml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
####################################################################
|
||||
# WARNING: These are designed specifically for Ansible tests #
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
- block:
|
||||
- name: install monit
|
||||
become: yes
|
||||
package:
|
||||
name: monit
|
||||
state: present
|
||||
|
||||
- name: monit config
|
||||
become: yes
|
||||
template:
|
||||
src: "monitrc.j2"
|
||||
dest: "/etc/monit/monitrc"
|
||||
|
||||
- name: process monit config
|
||||
become: yes
|
||||
template:
|
||||
src: "httpd_echo.j2"
|
||||
dest: "/etc/monit/conf.d/httpd_echo"
|
||||
|
||||
- name: copy process file
|
||||
become: yes
|
||||
copy:
|
||||
src: "{{item}}"
|
||||
dest: "/opt/{{item}}"
|
||||
loop:
|
||||
- daemon.py
|
||||
- httpd_echo.py
|
||||
|
||||
- name: restart monit
|
||||
become: yes
|
||||
service:
|
||||
name: monit
|
||||
state: restarted
|
||||
|
||||
- include_tasks: test.yml
|
||||
|
||||
always:
|
||||
- name: stop monit
|
||||
become: yes
|
||||
service:
|
||||
name: monit
|
||||
state: stopped
|
||||
|
||||
- name: uninstall monit
|
||||
become: yes
|
||||
package:
|
||||
name: monit
|
||||
state: absent
|
||||
26
tests/integration/targets/monit/tasks/test.yml
Normal file
26
tests/integration/targets/monit/tasks/test.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# order is important
|
||||
- import_tasks: test_state.yml
|
||||
vars:
|
||||
state: stopped
|
||||
initial_state: up
|
||||
expected_state: down
|
||||
|
||||
- import_tasks: test_state.yml
|
||||
vars:
|
||||
state: started
|
||||
initial_state: down
|
||||
expected_state: up
|
||||
|
||||
- import_tasks: test_state.yml
|
||||
vars:
|
||||
state: unmonitored
|
||||
initial_state: up
|
||||
expected_state: down
|
||||
|
||||
- import_tasks: test_state.yml
|
||||
vars:
|
||||
state: monitored
|
||||
initial_state: down
|
||||
expected_state: up
|
||||
|
||||
- import_tasks: test_errors.yml
|
||||
6
tests/integration/targets/monit/tasks/test_errors.yml
Normal file
6
tests/integration/targets/monit/tasks/test_errors.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
- name: Check an error occurs when wrong process name is used
|
||||
monit:
|
||||
name: missing
|
||||
state: started
|
||||
register: result
|
||||
failed_when: result is not skip and (result is success or result is not failed)
|
||||
51
tests/integration/targets/monit/tasks/test_state.yml
Normal file
51
tests/integration/targets/monit/tasks/test_state.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
- name: verify initial state (up)
|
||||
command: "curl -sf http://localhost:8082/hello"
|
||||
args:
|
||||
warn: false
|
||||
when: initial_state == 'up'
|
||||
|
||||
- name: verify initial state (down)
|
||||
command: "curl -sf http://localhost:8082/hello"
|
||||
args:
|
||||
warn: false
|
||||
register: curl_result
|
||||
failed_when: curl_result == 0
|
||||
when: initial_state == 'down'
|
||||
|
||||
- name: change httpd_echo process state to {{ state }}
|
||||
monit:
|
||||
name: httpd_echo
|
||||
state: "{{ state }}"
|
||||
register: result
|
||||
|
||||
- name: check that state changed
|
||||
assert:
|
||||
that:
|
||||
- result is success
|
||||
- result is changed
|
||||
|
||||
- name: check that service is {{ state }} (expected 'up')
|
||||
command: "curl -sf http://localhost:8082/hello"
|
||||
args:
|
||||
warn: false
|
||||
when: expected_state == 'up'
|
||||
|
||||
- name: check that service is {{ state }} (expected 'down')
|
||||
command: "curl -sf http://localhost:8082/hello"
|
||||
args:
|
||||
warn: false
|
||||
register: curl_result
|
||||
failed_when: curl_result == 0
|
||||
when: expected_state == 'down'
|
||||
|
||||
- name: try change state again to {{ state }}
|
||||
monit:
|
||||
name: httpd_echo
|
||||
state: "{{ state }}"
|
||||
register: result
|
||||
|
||||
- name: check that state is not changed
|
||||
assert:
|
||||
that:
|
||||
- result is success
|
||||
- result is not changed
|
||||
4
tests/integration/targets/monit/templates/httpd_echo.j2
Normal file
4
tests/integration/targets/monit/templates/httpd_echo.j2
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
check process httpd_echo with pidfile /tmp/httpd_echo.pid
|
||||
start program = "{{ansible_python.executable}} /opt/httpd_echo.py start"
|
||||
stop program = "{{ansible_python.executable}} /opt/httpd_echo.py stop"
|
||||
if failed host localhost port 8082 then restart
|
||||
14
tests/integration/targets/monit/templates/monitrc.j2
Normal file
14
tests/integration/targets/monit/templates/monitrc.j2
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
set daemon 2
|
||||
set logfile /var/log/monit.log
|
||||
set idfile /var/lib/monit/id
|
||||
set statefile /var/lib/monit/state
|
||||
|
||||
set eventqueue
|
||||
basedir /var/lib/monit/events
|
||||
slots 100
|
||||
|
||||
set httpd port 2812 and
|
||||
use address localhost
|
||||
allow localhost
|
||||
|
||||
include /etc/monit/conf.d/*
|
||||
|
|
@ -55,16 +55,21 @@ class MonitTest(unittest.TestCase):
|
|||
|
||||
def test_reload(self):
|
||||
self.module.run_command.return_value = (0, '', '')
|
||||
with self.patch_status(monit.Status.OK) as get_status:
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.monit.reload()
|
||||
|
||||
def test_wait_for_status(self):
|
||||
self.monit._sleep_time = 0
|
||||
status = [
|
||||
monit.Status.MISSING,
|
||||
monit.Status.DOES_NOT_EXIST,
|
||||
monit.Status.INITIALIZING,
|
||||
monit.Status.OK.pending(),
|
||||
monit.Status.OK
|
||||
]
|
||||
with self.patch_status(status) as get_status:
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.monit.reload()
|
||||
self.monit.wait_for_monit_to_stop_pending('ok')
|
||||
self.assertEqual(get_status.call_count, len(status))
|
||||
|
||||
def test_monitor(self):
|
||||
|
|
@ -105,6 +110,8 @@ BASIC_OUTPUT_CASES = [
|
|||
(TEST_OUTPUT % ('processX', 'Monitored - stop pending'), monit.Status.NOT_MONITORED),
|
||||
(TEST_OUTPUT % ('processX', 'Monitored - restart pending'), monit.Status.OK),
|
||||
(TEST_OUTPUT % ('processX', 'Not Monitored - monitor pending'), monit.Status.OK),
|
||||
(TEST_OUTPUT % ('processX', 'Does not exist'), monit.Status.DOES_NOT_EXIST),
|
||||
(TEST_OUTPUT % ('processX', 'Not monitored'), monit.Status.NOT_MONITORED),
|
||||
])
|
||||
def test_parse_status(output, expected):
|
||||
status = monit.Monit(None, '', 'processX', 0)._parse_status(output)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue