1
0
Fork 0
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:
Simon Kelly 2020-10-16 15:04:06 +02:00
parent 00152dbb63
commit 816f31c19f
13 changed files with 393 additions and 25 deletions

View file

@ -0,0 +1 @@
destructive

View 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()."""

View 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)

View 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

View file

@ -0,0 +1,2 @@
dependencies:
- setup_pkg_mgr

View 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

View 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

View 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)

View 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

View 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

View 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/*

View file

@ -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)