mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-02-03 05:03:43 +08:00
143 lines
4.4 KiB
Python
143 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""
|
|
This module defines constant for events emitted by external services that work
|
|
with MBS together to complete a module build.
|
|
|
|
The event name is defined in general as much as possible, especially for the
|
|
events from Koji. Because some instance based on Koji, like Brew, might send
|
|
messages to different topics on different message bus. For example, when a
|
|
build is complete, Koji sends a message to topic buildsys.build.state.change,
|
|
however Brew sends to topic brew.build.complete, etc.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from functools import wraps
|
|
import sched
|
|
import threading
|
|
import time
|
|
|
|
from module_build_service.common import log
|
|
|
|
|
|
KOJI_BUILD_CHANGE = "koji_build_change"
|
|
KOJI_TAG_CHANGE = "koji_tag_change"
|
|
KOJI_REPO_CHANGE = "koji_repo_change"
|
|
MBS_MODULE_STATE_CHANGE = "mbs_module_state_change"
|
|
|
|
|
|
class Scheduler(sched.scheduler):
|
|
"""
|
|
Subclass of `sched.scheduler` allowing to schedule handlers calls.
|
|
|
|
If one of the MBS handler functions need to call another handler, they need to do it in a safe
|
|
way - such another handler call should not be done in the middle of another handler's
|
|
execution.
|
|
|
|
This class provides an solution for that. Handler can schedule run of other handler using
|
|
the `add` method. The handlers should use `mbs_event_handler` decorator which ensures that
|
|
the `run` method is called at the end of handler's execution and other scheduler handlers
|
|
are executed.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
sched.scheduler.__init__(self, *args, **kwargs)
|
|
self.local = threading.local()
|
|
|
|
def add(self, handler, arguments=()):
|
|
"""
|
|
Schedule execution of `handler` with `arguments`.
|
|
"""
|
|
self.enter(0, 0, handler.delay, arguments)
|
|
|
|
def run(self):
|
|
"""
|
|
Runs scheduled handlers.
|
|
"""
|
|
if getattr(self.local, 'running', False):
|
|
return
|
|
|
|
try:
|
|
self.local.running = True
|
|
# Note that events that are added during the execution of the
|
|
# handlers are executed as part of the .run() call without
|
|
# further logging.
|
|
log.debug("Running event scheduler with following events:")
|
|
for event in self.queue:
|
|
log.debug(" %r", event)
|
|
sched.scheduler.run(self)
|
|
finally:
|
|
self.local.running = False
|
|
|
|
def reset(self):
|
|
"""
|
|
Resets the Scheduler to initial state.
|
|
"""
|
|
while not self.empty():
|
|
self.cancel(self.queue[0])
|
|
|
|
|
|
scheduler = Scheduler(time.time, delayfunc=lambda x: x)
|
|
|
|
|
|
class EventTrap():
|
|
"""
|
|
A context handler that can be used to provide a place to store exceptions
|
|
that occur in event handlers during a section of code. This is global rather
|
|
than per-thread, because we we set up the trap in the main thread, but
|
|
event event handlers are processed in the thread where moksha runs MBSConsumer.
|
|
|
|
This is needed because @celery_app.task simply logs and ignores exceptions.
|
|
"""
|
|
lock = threading.Lock()
|
|
current = None
|
|
|
|
def __init__(self):
|
|
self.exception = None
|
|
|
|
def __enter__(self):
|
|
with EventTrap.lock:
|
|
EventTrap.current = self
|
|
return self
|
|
|
|
def __exit__(self, type, value, tb):
|
|
with EventTrap.lock:
|
|
EventTrap.current = None
|
|
|
|
@classmethod
|
|
def set_exception(cls, exception):
|
|
with cls.lock:
|
|
if cls.current and not cls.current.exception:
|
|
cls.current.exception = exception
|
|
|
|
@classmethod
|
|
def get_exception(cls):
|
|
with cls.lock:
|
|
if cls.current:
|
|
return cls.current.exception
|
|
else:
|
|
return None
|
|
|
|
|
|
def mbs_event_handler(func):
|
|
"""
|
|
A decorator for MBS event handlers. It implements common tasks which should otherwise
|
|
be repeated in every MBS event handler, for example:
|
|
|
|
- at the end of handler, call events.scheduler.run().
|
|
"""
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
try:
|
|
return func(*args, **kwargs)
|
|
except Exception as e:
|
|
EventTrap.set_exception(e)
|
|
raise e
|
|
finally:
|
|
scheduler.run()
|
|
# save origin function as functools.wraps from python2 doesn't preserve the signature
|
|
if not hasattr(wrapper, "__wrapped__"):
|
|
wrapper.__wrapped__ = func
|
|
return wrapper
|