Add support for other extensions

This commit is contained in:
askiiart 2024-01-30 13:35:56 -06:00
parent ffa21bffc6
commit ed220154d8
Signed by untrusted user who does not match committer: askiiart
GPG key ID: BC3800E55FB54D67
5 changed files with 161 additions and 14 deletions

4
.gitignore vendored
View file

@ -1 +1,3 @@
extensions/*
extensions/
__pycache__/
logs/

View file

@ -14,7 +14,7 @@ Not much, you?
Docs are in the `./docs/` folder, but here's a quick overview on how to use this:
1. Clone this repository - or just download `updog.py`
1. Clone this repository - `git clone --depth 1 https://git.askiiart.net/askiiart/updog`
2. Install your extensions in the appropriate folders: `./extensions/checkers`, `./extensions/alerts` (optional), and `./extensions/logging`
3. Create your `services.json` file, example below.
@ -49,6 +49,10 @@ Docs are in the `./docs/` folder, but here's a quick overview on how to use this
- Call the extensions
- Add support for logging and alert extensions
- Add maintenance windows (optionally recurring)
- Add ability to set default extensions that can be overridden on a per-service basis
- And groups (lower priority)
- Add ability for checkers to have preferred alerts and logging extensions - will print a warning if the preferred extension(s) are not used
- Add "keywords" to be replaced in the config (for current directory, service name, maybe some others)
---

21
services-example.json Normal file
View file

@ -0,0 +1,21 @@
{
"site": {
"name": "A Website",
"checker": "CheckerTemplate",
"checker-args": {
"url": "https://example.net",
"port": 443,
"lol": "CheckerTemplate ignores these options lol"
},
"rate": 60,
"alerts": "AlertsTemplate",
"alerts-args": {
"url": "https://example.com/webhook-url-or-whatever-goes-here",
"lol": "irrelevant, AlertsTemplate ignores these options lol"
},
"logging": "LoggingTemplate",
"logging-args": {
"file": "/home/askiiart/Documents/updog/logs/site-log"
}
}
}

54
timer.py Normal file
View file

@ -0,0 +1,54 @@
import threading
import time
# Threaded repeating timer thing
# From https://stackoverflow.com/a/40965385/16432246
# It shouldn't drift, but that's untested
# Keep in mind you can't catch Exceptions for this, it just crashes the thread.
# Some more scheduling stuff: https://www.redwood.com/article/python-job-scheduling/
class RepeatedTimer(object):
'''
Run stuff repeatedly every x seconds
Example usage (from SO and ported to Python 3):
from time import sleep
def hello(name):
print "Hello %s!" % name
print()"starting...")
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
sleep(5) # your long-running job goes here...
catch:
rt.stop() # better in a try/catch block to make sure the program ends!
'''
def __init__(self, interval, function, *args, **kwargs):
'''
Run a functions with arguments every
'''
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.next_call = time.time()
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self.next_call += self.interval
self._timer = threading.Timer(
self.next_call - time.time(), self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False

View file

@ -1,31 +1,97 @@
from timer import RepeatedTimer
import sys
import os
import importlib
import inspect
import json
DEBUG = False
# relative imports suck - https://gideonbrimleaf.github.io/2021/01/26/relative-imports-python.html
path = os.path.realpath(__file__)
path = path[:path.rfind('/')]
#####################
# Import extensions #
#####################
# Import checkers #
# relative imports suck - https://gideonbrimleaf.github.io/2021/01/26/relative-imports-python.html
sys.path.insert(1, f'{path}/extensions/checkers')
# importlib used to import stuff programmatically, rather than using the hardcoded import keyword, basically the same but it seems it can't import *just* a class
uptime_extension_imports = []
checker_extension_imports = []
for ext_name in os.listdir(f'{path}/extensions/checkers'):
if ext_name[0] != '.':
uptime_extension_imports.append(f'{ext_name}.{ext_name}')
checker_extension_imports.append(f'{ext_name}.{ext_name}')
# uptime_checkers contains all the classes for the checker extensions
# checkers contains all the classes for the checker extensions
# e.g. {'CheckerTemplate': checker_template.checker_template.CheckerTemplate}
checkers = dict()
for ext in [importlib.import_module(ext) for ext in uptime_extension_imports]:
checkers = {}
for ext in [importlib.import_module(ext) for ext in checker_extension_imports]:
for name, obj in inspect.getmembers(ext):
if inspect.isclass(obj):
checkers[name] = obj
if DEBUG:
print('uptime_extension_imports:', uptime_extension_imports)
print('checkers:', checkers)
# Import alerts #
# same as above, just for alerts
sys.path.insert(1, f'{path}/extensions/alerts')
print(checkers['CheckerTemplate'].get_status())
alerts_extension_imports = []
for ext_name in os.listdir(f'{path}/extensions/alerts'):
if ext_name[0] != '.':
alerts_extension_imports.append(f'{ext_name}.{ext_name}')
# alerts contains all the classes for the checker extensions
# e.g. {'AlertsTemplate': alerts_template.alerts_template.AlertsTemplate}
alerts = {}
for ext in [importlib.import_module(ext) for ext in alerts_extension_imports]:
for name, obj in inspect.getmembers(ext):
if inspect.isclass(obj):
alerts[name] = obj
# Import logging #
# same as above, just for logging
sys.path.insert(1, f'{path}/extensions/logging')
logging_extension_imports = []
for ext_name in os.listdir(f'{path}/extensions/logging'):
if ext_name[0] != '.':
logging_extension_imports.append(f'{ext_name}.{ext_name}')
# logging contains all the classes for the checker extensions
# e.g. {'LoggingTemplate': logging_template.logging_template.LoggingTemplate}
logging = {}
for ext in [importlib.import_module(ext) for ext in logging_extension_imports]:
for name, obj in inspect.getmembers(ext):
if inspect.isclass(obj):
logging[name] = obj
# get config from services.json
if 'services.json' in os.listdir():
config_filename = 'services.json'
elif 'services-example.json' in os.listdir():
config_filename = 'services-example.json'
with open(config_filename, 'rt') as config_file:
config = json.loads(''.join(config_file.readlines()))
def create_instances(config):
'''
Creates instances of all the extensions according to config
Parameters:
config: the dictionary containing the config
Returns:
instances (dict): A dictionary containing instances of the extensions
example: {'site': {'checker': instanceOfCheckerTemplate}}
'''
instances = {}
for service in config.keys():
instances[service] = {}
# just creates an instance of the checker with the arguments for it
instances[service]['checker'] = checkers[config[service]
['checker']](config[service]['checker-args'])
instances[service]['alerts'] = alerts[config[service]
['alerts']](config[service]['alerts-args'])
instances[service]['logging'] = logging[config[service]
['logging']](config[service]['logging-args'])
return instances