Source code for toxicnotifications.cmds
# -*- coding: utf-8 -*-
# Copyright 2023 Juca Crispim <juca@poraodojuca.dev>
# This file is part of toxicbuild.
# toxicbuild is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# toxicbuild is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with toxicbuild. If not, see <http://www.gnu.org/licenses/>.
# pylint: disable=all
import asyncio
import os
import importlib.resources
import shutil
import sys
from time import sleep
from uuid import uuid4
from toxiccommon import common_setup
from toxiccore.cmd import command, main
from toxiccore.utils import (changedir, log, daemonize as daemon,
MonkeyPatcher, set_loglevel)
from toxicnotifications import create_settings_and_connect
PIDFILE = 'toxicnotifications.pid'
LOGFILE = './toxicnotifications.log'
[docs]class SettingsPatcher(MonkeyPatcher):
"""Patches the settings from pyrocumulus to use the same settings
as toxibuild."""
[docs] def patch_pyro_settings(self, settings):
from pyrocumulus import conf as pyroconf
self.patch_item(pyroconf, 'settings', settings)
[docs]@command
def create(root_dir):
"""Creates a new toxicnotifications environment.
:param --root_dir: Root directory for toxicnotifications."""
print('Creating root_dir `{}` for toxicnotifications'.format(root_dir))
os.makedirs(root_dir)
template_fname = 'toxicnotifications.conf.tmpl'
template_dir = importlib.resources.files(
'toxicnotifications').joinpath('templates')
template_file = os.path.join(template_dir, template_fname)
dest_file = os.path.join(root_dir, 'toxicnotifications.conf')
shutil.copyfile(template_file, dest_file)
[docs]@command
def start(workdir, daemonize=False, stdout=LOGFILE, stderr=LOGFILE,
conffile=None, loglevel='info', pidfile=PIDFILE):
""" Starts toxicnotifications.
:param workdir: Work directory for server.
:param --daemonize: Run as daemon. Defaults to False
:param --stdout: stdout path. Defaults to /dev/null
:param --stderr: stderr path. Defaults to /dev/null
:param -c, --conffile: path to config file. Defaults to None.
If not conffile, will look for a file called ``toxicnotifications.conf``
inside ``workdir``
:param --loglevel: Level for logging messages. Defaults to `info`.
:param --pidfile: Name of the file to use as pidfile. Defaults to
``toxicnotifications.pid``
"""
if not os.path.exists(workdir):
print('Workdir `{}` does not exist'.format(workdir))
sys.exit(1)
_set_conffile_env(workdir, conffile)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
create_settings_and_connect()
from toxicnotifications import settings
addr = settings.ADDR
port = settings.PORT
try:
use_ssl = settings.USE_SSL
except AttributeError:
use_ssl = False
try:
certfile = settings.CERTFILE
except AttributeError:
certfile = None
try:
keyfile = settings.KEYFILE
except AttributeError:
keyfile = None
if daemonize:
daemon(call=notifications_init, cargs=(addr, port),
ckwargs={'use_ssl': use_ssl, 'certfile': certfile,
'keyfile': keyfile, 'loglevel': loglevel},
stdout=stdout, stderr=stderr, workdir=workdir, pidfile=pidfile)
else:
with changedir(workdir):
notifications_init(addr, port, use_ssl=use_ssl,
certfile=certfile, keyfile=keyfile,
loglevel=loglevel)
[docs]@command
def stop(workdir, pidfile=PIDFILE, kill=False):
""" Stops toxicnotifications.
:param workdir: Work directory for the ui to be killed.
:param --pidfile: pid file for the process.
:param kill: If true, send signum 9, otherwise, 15.
"""
if not os.path.exists(workdir):
print('Workdir `{}` does not exist'.format(workdir))
sys.exit(1)
workdir = os.path.abspath(workdir)
with changedir(workdir):
sys.path.append(workdir)
os.environ['TOXICNOTIFICATIONS_SETTINGS'] = os.path.join(
workdir, 'toxicnotifications.conf')
create_settings_and_connect()
print('Stopping output')
with changedir(workdir):
with open(pidfile) as fd:
pid = int(fd.read())
sig = 9 if kill else 15
os.kill(pid, sig)
if sig != 9:
print('Waiting for the process shutdown')
while _process_exist(pid):
sleep(0.5)
os.remove(pidfile)
[docs]@command
def restart(workdir, pidfile=PIDFILE, loglevel='info'):
"""Restarts toxicnotifications
The instance of toxicnotifications in ``workdir`` will be restarted.
:param workdir: Workdir for instance to be killed.
:param --pidfile: Name of the file to use as pidfile.
:param --loglevel: Level for logging messages.
"""
stop(workdir, pidfile=pidfile)
start(workdir, pidfile=pidfile, daemonize=True, loglevel=loglevel)
[docs]@command
def create_token(workdir, conffile=None):
"""Creates an access token for the notifications api.
:param workdir: Work directory for server.
:param -c, --conffile: path to config file. Defaults to None.
If not conffile, will look for a file called ``toxicnotifications.conf``
inside ``workdir``."""
_set_conffile_env(workdir, conffile)
create_settings_and_connect()
loop = asyncio.get_event_loop()
uncrypted_token = loop.run_until_complete(create_auth_token())
print('Created access token: {}'.format(uncrypted_token))
print('TOKEN:{}'.format(uncrypted_token))
[docs]async def create_auth_token(workdir=None):
if workdir:
_set_conffile_env(workdir, None)
create_settings_and_connect()
from pyrocumulus.auth import AccessToken, Permission
from toxicnotifications import Notification
try:
token = AccessToken(name='notifications-token-{}'.format(uuid4().hex))
uncrypted_token = await token.save()
await Permission.create_perms_to(token, Notification, 'crud')
finally:
os.environ['TOXICMASTER_SETTINGS'] = ''
return uncrypted_token
[docs]def output_handler_init(handler):
"""Starts the output server"""
asyncio.ensure_future(handler.run())
log('Toxicnotifications is running.')
[docs]def notifications_init(addr, port, use_ssl, certfile, keyfile, loglevel):
from toxicnotifications.server import run_server
from toxicnotifications import settings
set_loglevel(loglevel)
loop = asyncio.get_event_loop()
loop.run_until_complete(common_setup(settings))
run_server(addr, port, use_ssl=use_ssl,
certfile=certfile, keyfile=keyfile)
def _check_conffile(workdir, conffile):
"""Checks if the conffile is inside workdir."""
absworkdir = os.path.abspath(workdir)
absconffile = os.path.abspath(conffile)
return absconffile.startswith(absworkdir)
def _set_conffile_env(workdir, conffile):
if conffile:
is_in_workdir = _check_conffile(workdir, conffile)
if not is_in_workdir:
print('Config file must be inside workdir')
sys.exit(1)
os.environ['TOXICNOTIFICATIONS_SETTINGS'] = os.path.join(
workdir, conffile)
else:
os.environ['TOXICNOTIFICATIONS_SETTINGS'] = os.path.join(
workdir, 'toxicnotifications.conf')
def _process_exist(pid):
try:
os.kill(pid, 0)
r = True
except OSError:
r = False
return r
if __name__ == '__main__':
main()