initial import for Open Source 🎉
This commit is contained in:
parent
1898c361f3
commit
9c0dd3b722
2048 changed files with 218743 additions and 0 deletions
0
workers/notificationworker/__init__.py
Normal file
0
workers/notificationworker/__init__.py
Normal file
50
workers/notificationworker/models_interface.py
Normal file
50
workers/notificationworker/models_interface.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from collections import namedtuple
|
||||
from six import add_metaclass
|
||||
|
||||
|
||||
class Repository(namedtuple('Repository', ['namespace_name', 'name'])):
|
||||
"""
|
||||
Repository represents a repository.
|
||||
"""
|
||||
|
||||
|
||||
class Notification(
|
||||
namedtuple('Notification', [
|
||||
'uuid', 'event_name', 'method_name', 'event_config_dict', 'method_config_dict',
|
||||
'repository'])):
|
||||
"""
|
||||
Notification represents a registered notification of some kind.
|
||||
"""
|
||||
|
||||
|
||||
@add_metaclass(ABCMeta)
|
||||
class NotificationWorkerDataInterface(object):
|
||||
"""
|
||||
Interface that represents all data store interactions required by the notification worker.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_enabled_notification(self, notification_uuid):
|
||||
""" Returns an *enabled* notification with the given UUID, or None if none. """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def reset_number_of_failures_to_zero(self, notification):
|
||||
""" Resets the number of failures for the given notification back to zero. """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def increment_notification_failure_count(self, notification):
|
||||
""" Increments the number of failures on the given notification. """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def create_notification_for_testing(self, target_username, method_name=None, method_config=None):
|
||||
""" Creates a notification for testing. """
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def user_has_local_notifications(self, target_username):
|
||||
""" Returns whether there are any Quay-local notifications for the given user. """
|
||||
pass
|
50
workers/notificationworker/models_pre_oci.py
Normal file
50
workers/notificationworker/models_pre_oci.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import json
|
||||
|
||||
from data import model
|
||||
from workers.notificationworker.models_interface import (
|
||||
NotificationWorkerDataInterface, Notification, Repository)
|
||||
|
||||
def notification(notification_row):
|
||||
""" Converts the given notification row into a notification tuple. """
|
||||
return Notification(uuid=notification_row.uuid, event_name=notification_row.event.name,
|
||||
method_name=notification_row.method.name,
|
||||
event_config_dict=json.loads(notification_row.event_config_json or '{}'),
|
||||
method_config_dict=json.loads(notification_row.config_json or '{}'),
|
||||
repository=Repository(notification_row.repository.namespace_user.username,
|
||||
notification_row.repository.name))
|
||||
|
||||
class PreOCIModel(NotificationWorkerDataInterface):
|
||||
def get_enabled_notification(self, notification_uuid):
|
||||
try:
|
||||
notification_row = model.notification.get_enabled_notification(notification_uuid)
|
||||
except model.InvalidNotificationException:
|
||||
return None
|
||||
|
||||
return notification(notification_row)
|
||||
|
||||
def reset_number_of_failures_to_zero(self, notification):
|
||||
model.notification.reset_notification_number_of_failures(
|
||||
notification.repository.namespace_name, notification.repository.name, notification.uuid)
|
||||
|
||||
def increment_notification_failure_count(self, notification):
|
||||
model.notification.increment_notification_failure_count(notification.uuid)
|
||||
|
||||
def create_notification_for_testing(self, target_username, method_name='quay_notification',
|
||||
method_config=None):
|
||||
repo = model.repository.get_repository('devtable', 'simple')
|
||||
method_data = method_config or {
|
||||
'target': {
|
||||
'kind': 'user',
|
||||
'name': target_username,
|
||||
}
|
||||
}
|
||||
notification = model.notification.create_repo_notification(repo, 'repo_push',
|
||||
method_name, method_data, {})
|
||||
return notification.uuid
|
||||
|
||||
def user_has_local_notifications(self, target_username):
|
||||
user = model.user.get_namespace_user(target_username)
|
||||
return bool(list(model.notification.list_notifications(user)))
|
||||
|
||||
|
||||
pre_oci_model = PreOCIModel()
|
43
workers/notificationworker/notificationworker.py
Normal file
43
workers/notificationworker/notificationworker.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import logging
|
||||
|
||||
from app import notification_queue
|
||||
from notifications.notificationmethod import NotificationMethod, InvalidNotificationMethodException
|
||||
from notifications.notificationevent import NotificationEvent, InvalidNotificationEventException
|
||||
from workers.notificationworker.models_pre_oci import pre_oci_model as model
|
||||
from workers.queueworker import QueueWorker, JobException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NotificationWorker(QueueWorker):
|
||||
def process_queue_item(self, job_details):
|
||||
notification = model.get_enabled_notification(job_details['notification_uuid'])
|
||||
if notification is None:
|
||||
return
|
||||
|
||||
event_name = notification.event_name
|
||||
method_name = notification.method_name
|
||||
|
||||
try:
|
||||
event_handler = NotificationEvent.get_event(event_name)
|
||||
method_handler = NotificationMethod.get_method(method_name)
|
||||
except InvalidNotificationMethodException as ex:
|
||||
logger.exception('Cannot find notification method: %s', ex.message)
|
||||
raise JobException('Cannot find notification method: %s' % ex.message)
|
||||
except InvalidNotificationEventException as ex:
|
||||
logger.exception('Cannot find notification event: %s', ex.message)
|
||||
raise JobException('Cannot find notification event: %s' % ex.message)
|
||||
|
||||
if event_handler.should_perform(job_details['event_data'], notification):
|
||||
try:
|
||||
method_handler.perform(notification, event_handler, job_details)
|
||||
model.reset_number_of_failures_to_zero(notification)
|
||||
except (JobException, KeyError) as exc:
|
||||
model.increment_notification_failure_count(notification)
|
||||
raise exc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
worker = NotificationWorker(notification_queue, poll_period_seconds=10, reservation_seconds=30,
|
||||
retry_after_seconds=30)
|
||||
worker.start()
|
72
workers/notificationworker/test/test_notificationworker.py
Normal file
72
workers/notificationworker/test/test_notificationworker.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
import pytest
|
||||
|
||||
from mock import patch, Mock
|
||||
from httmock import urlmatch, HTTMock
|
||||
|
||||
from notifications.notificationmethod import (QuayNotificationMethod, EmailMethod, WebhookMethod,
|
||||
FlowdockMethod, HipchatMethod, SlackMethod,
|
||||
CannotValidateNotificationMethodException)
|
||||
from notifications.notificationevent import RepoPushEvent
|
||||
from notifications.models_interface import Repository
|
||||
from workers.notificationworker.notificationworker import NotificationWorker
|
||||
|
||||
from test.fixtures import *
|
||||
|
||||
from workers.notificationworker.models_pre_oci import pre_oci_model as model
|
||||
|
||||
def test_basic_notification_endtoend(initialized_db):
|
||||
# Ensure the public user doesn't have any notifications.
|
||||
assert not model.user_has_local_notifications('public')
|
||||
|
||||
# Add a basic build notification.
|
||||
notification_uuid = model.create_notification_for_testing('public')
|
||||
event_data = {}
|
||||
|
||||
# Fire off the queue processing.
|
||||
worker = NotificationWorker(None)
|
||||
worker.process_queue_item({
|
||||
'notification_uuid': notification_uuid,
|
||||
'event_data': event_data,
|
||||
})
|
||||
|
||||
# Ensure the notification was handled.
|
||||
assert model.user_has_local_notifications('public')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('method,method_config,netloc', [
|
||||
(QuayNotificationMethod, {'target': {'name': 'devtable', 'kind': 'user'}}, None),
|
||||
(EmailMethod, {'email': 'jschorr@devtable.com'}, None),
|
||||
(WebhookMethod, {'url': 'http://example.com'}, 'example.com'),
|
||||
(FlowdockMethod, {'flow_api_token': 'sometoken'}, 'api.flowdock.com'),
|
||||
(HipchatMethod, {'notification_token': 'token', 'room_id': 'foo'}, 'api.hipchat.com'),
|
||||
(SlackMethod, {'url': 'http://example.com'}, 'example.com'),
|
||||
])
|
||||
def test_notifications(method, method_config, netloc, initialized_db):
|
||||
url_hit = [False]
|
||||
@urlmatch(netloc=netloc)
|
||||
def url_handler(_, __):
|
||||
url_hit[0] = True
|
||||
return ''
|
||||
|
||||
mock = Mock()
|
||||
def get_mock(*args, **kwargs):
|
||||
return mock
|
||||
|
||||
with patch('notifications.notificationmethod.Message', get_mock):
|
||||
with HTTMock(url_handler):
|
||||
# Add a basic build notification.
|
||||
notification_uuid = model.create_notification_for_testing('public',
|
||||
method_name=method.method_name(),
|
||||
method_config=method_config)
|
||||
event_data = RepoPushEvent().get_sample_data('devtable', 'simple', {})
|
||||
|
||||
# Fire off the queue processing.
|
||||
worker = NotificationWorker(None)
|
||||
worker.process_queue_item({
|
||||
'notification_uuid': notification_uuid,
|
||||
'event_data': event_data,
|
||||
'performer_data': {},
|
||||
})
|
||||
|
||||
if netloc is not None:
|
||||
assert url_hit[0]
|
Reference in a new issue