diff --git a/README.md b/README.md index cb7962f..699ee64 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ reload: watch_config_files: true # Optional, watch defined config files, default True files: # Optional, list of files to watch - /etc/conf/to/watch + - /file/support/*.matching # can also be enabled like this: reload: true diff --git a/docs/config.rst b/docs/config.rst index 93fdb15..e391158 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -236,6 +236,7 @@ Accept boolean or dictionary watch_config_files: true # Optional, watch defined config files, default True files: # Optional, list of files to watch - /etc/conf/to/watch + - /file/support/*.matching # can also be enabled with a boolean: reload: true diff --git a/pyentrypoint/reloader.py b/pyentrypoint/reloader.py index a64d34c..6e4b866 100644 --- a/pyentrypoint/reloader.py +++ b/pyentrypoint/reloader.py @@ -4,6 +4,7 @@ from __future__ import absolute_import from __future__ import unicode_literals +import glob import os import signal from multiprocessing import Process @@ -18,14 +19,14 @@ class Reload(FileSystemEventHandler): """Reload object""" - def __init__(self, sig, files, pid=1): + def __init__(self, sig, reloader, pid=1): self.signal = sig - self.files = files + self.reloader = reloader self.pid = pid self.log = Logs().log def on_any_event(self, event): - if event.src_path in self.files: + if event.src_path in self.reloader.files: self.log.info( 'File {file} has changed, send sig {sig} to pid {pid}'.format( file=event.src_path, @@ -34,6 +35,12 @@ class Reload(FileSystemEventHandler): ) ) os.kill(self.pid, self.signal) + else: + self.log.debug( + 'Reloader triggered but file {file} not watched'.format( + file=file + ) + ) class Reloader(object): @@ -44,6 +51,7 @@ class Reloader(object): if not files: raise Exception('No file to watch for reload') + self.log = Logs().log self.proc = None self._files = files sig_attr = getattr(signal, sig) @@ -52,13 +60,18 @@ class Reloader(object): except: raise Exception('Wrong signal provided for reload') self.observer = Observer() - rel = Reload(sig=sig_attr, files=self.files) + rel = Reload(sig=sig_attr, reloader=self) for dir in self.dirs: + self.log.debug('Registering watcher for {dir}'.format(dir=dir)) self.observer.schedule(rel, dir, recursive=False) def _get_files(self): """Return iterator of tuples (path, file)""" for f in self._files: + for m in glob.iglob(f): + if os.path.isdir(m): + yield (m, m) + yield (os.path.dirname(m), m) if os.path.isdir(f): yield (f, f) yield (os.path.dirname(f), f) diff --git a/setup.py b/setup.py index 6678754..5470af6 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup # Thanks Sam and Max -__version__ = '0.4.3' +__version__ = '0.4.4' if __name__ == '__main__': setup( diff --git a/tests/configs/reloader/reloader_config.yml b/tests/configs/reloader/reloader_config.yml index 348dffb..7dfada5 100644 --- a/tests/configs/reloader/reloader_config.yml +++ b/tests/configs/reloader/reloader_config.yml @@ -4,7 +4,8 @@ config_files: reload: watch_config_files: false files: - - /tmp/reload_custom + - /tmp/1/reload_custom + - /tmp/2/*.match pre_conf_commands: - touch /tmp/reload_custom diff --git a/tests/reloader_test.py b/tests/reloader_test.py index 1162121..311a2d5 100644 --- a/tests/reloader_test.py +++ b/tests/reloader_test.py @@ -21,18 +21,23 @@ from signal import SIGHUP from time import sleep +def _reloader_check(conf, command): + entry = Entrypoint(conf=conf) + entry.apply_conf() + entry.config.reload.run(ret=True) + subprocess.check_call(command) + sleep(1) + entry.config.reload.stop() + + def test_reloader(): if 'ENTRYPOINT_DISABLE_RELOAD' in os.environ: os.environ.pop('ENTRYPOINT_DISABLE_RELOAD') with mock.patch('os.kill') as os_kill: - entry = Entrypoint(conf='configs/reloader/reloader.yml') - entry.apply_conf() - entry.config.reload.run(ret=True) - subprocess.check_call(['touch', '/tmp/reload']) - sleep(1) - entry.config.reload.stop() + _reloader_check(conf='configs/reloader/reloader.yml', + command=['touch', '/tmp/reload']) os_kill.assert_called_once_with(1, SIGHUP) @@ -52,12 +57,21 @@ def test_reloader_custom(): if 'ENTRYPOINT_DISABLE_RELOAD' in os.environ: os.environ.pop('ENTRYPOINT_DISABLE_RELOAD') + subprocess.check_call(['mkdir', '-p', '/tmp/1', '/tmp/2']) + subprocess.check_call(['touch', '/tmp/2/tmp.match']) + with mock.patch('os.kill') as os_kill: - entry = Entrypoint(conf='configs/reloader/reloader_config.yml') - entry.apply_conf() - entry.run_pre_conf_cmds() - entry.config.reload.run(ret=True) - subprocess.check_call(['touch', '/tmp/reload', '/tmp/reload_custom']) - sleep(1) - entry.config.reload.stop() + _reloader_check(conf='configs/reloader/reloader_config.yml', + command=['touch', '/tmp/1/reload_custom']) + # triggered twice because file creation + os_kill.assert_called_with(1, SIGHUP) + + with mock.patch('os.kill') as os_kill: + _reloader_check(conf='configs/reloader/reloader_config.yml', + command=['touch', '/tmp/2/tmp.match']) os_kill.assert_called_once_with(1, SIGHUP) + + with mock.patch('os.kill') as os_kill: + _reloader_check(conf='configs/reloader/reloader_config.yml', + command=['touch', '/tmp/2/tmpnotmatch']) + assert not os_kill.called