initial import for Open Source 🎉

This commit is contained in:
Jimmy Zelinskie 2019-11-12 11:09:47 -05:00
parent 1898c361f3
commit 9c0dd3b722
2048 changed files with 218743 additions and 0 deletions

View file

View 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

View 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()

View 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()

View 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]