Reorganize connections into drivers
This change, while substantial, is mostly organizational. Currently, connections, sources, triggers, and reporters are discrete concepts, and yet are related by virtue of the fact that the ConnectionRegistry is used to instantiate each of them. The method used to instantiate them is called "_getDriver", in recognition that behind each "trigger", etc., which appears in the config file, there is a class in the zuul.trigger hierarchy implementing the driver for that trigger. Connections also specify a "driver" in the config file. In this change, we redefine a "driver" as a single class that organizes related connections, sources, triggers and reporters. The connection, source, trigger, and reporter interfaces still exist. A driver class is responsible for indicating which of those interfaces it supports and instantiating them when asked to do so. Zuul instantiates a single instance of each driver class it knows about (currently hardcoded, but in the future, we will be able to easily ask entrypoints for these). That instance will be retained for the life of the Zuul server process. When Zuul is (re-)configured, it asks the driver instances to create new connection, source, trigger, reporter instances as necessary. For instance, a user may specify a connection that uses the "gerrit" driver, and the ConnectionRegistry would call getConnection() on the Gerrit driver instance. This is done for two reasons: first, it allows us to organize all of the code related to interfacing with an external system together. All of the existing connection, source, trigger, and reporter classes are moved as follows: zuul.connection.FOO -> zuul.driver.FOO.FOOconnection zuul.source.FOO -> zuul.driver.FOO.FOOsource zuul.trigger.FOO -> zuul.driver.FOO.FOOtrigger zuul.reporter.FOO -> zuul.driver.FOO.FOOreporter For instance, all of the code related to interfacing with Gerrit is now is zuul.driver.gerrit. Second, the addition of a single, long-lived object associated with each of these systems allows us to better support some types of interfaces. For instance, the Zuul trigger maintains a list of events it is required to emit -- this list relates to a tenant as a whole rather than individual pipelines or triggers. The timer trigger maintains a single scheduler instance for all tenants, but must be able to add or remove cron jobs based on an individual tenant being reconfigured. The global driver instance for each of these can be used to accomplish this. As a result of using the driver interface to create new connection, source, trigger and reporter instances, the connection setup in ConnectionRegistry is much simpler, and can easily be extended with entrypoints in the future. The existing tests of connections, sources, triggers, and reporters which only tested that they could be instantiated and have names have been removed, as there are functional tests which cover them. Change-Id: Ib2f7297d81f7a003de48f799dc1b09e82d4894bc
This commit is contained in:
29
zuul/driver/smtp/__init__.py
Normal file
29
zuul/driver/smtp/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import smtpconnection
|
||||
import smtpreporter
|
||||
|
||||
|
||||
class SMTPDriver(object):
|
||||
name = 'smtp'
|
||||
|
||||
def getConnection(self, name, config):
|
||||
return smtpconnection.SMTPConnection(self, name, config)
|
||||
|
||||
def getReporter(self, connection, config=None):
|
||||
return smtpreporter.SMTPReporter(self, connection, config)
|
||||
|
||||
def getReporterSchema(self):
|
||||
return smtpreporter.getSchema()
|
||||
62
zuul/driver/smtp/smtpconnection.py
Normal file
62
zuul/driver/smtp/smtpconnection.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2014 Rackspace Australia
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import voluptuous as v
|
||||
import smtplib
|
||||
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
from zuul.connection import BaseConnection
|
||||
|
||||
|
||||
class SMTPConnection(BaseConnection):
|
||||
driver_name = 'smtp'
|
||||
log = logging.getLogger("connection.smtp")
|
||||
|
||||
def __init__(self, driver, connection_name, connection_config):
|
||||
super(SMTPConnection, self).__init__(driver, connection_name,
|
||||
connection_config)
|
||||
|
||||
self.smtp_server = self.connection_config.get(
|
||||
'server', 'localhost')
|
||||
self.smtp_port = self.connection_config.get('port', 25)
|
||||
self.smtp_default_from = self.connection_config.get(
|
||||
'default_from', 'zuul')
|
||||
self.smtp_default_to = self.connection_config.get(
|
||||
'default_to', 'zuul')
|
||||
|
||||
def sendMail(self, subject, message, from_email=None, to_email=None):
|
||||
# Create a text/plain email message
|
||||
from_email = from_email \
|
||||
if from_email is not None else self.smtp_default_from
|
||||
to_email = to_email if to_email is not None else self.smtp_default_to
|
||||
|
||||
msg = MIMEText(message)
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = from_email
|
||||
msg['To'] = to_email
|
||||
|
||||
try:
|
||||
s = smtplib.SMTP(self.smtp_server, self.smtp_port)
|
||||
s.sendmail(from_email, to_email.split(','), msg.as_string())
|
||||
s.quit()
|
||||
except:
|
||||
return "Could not send email via SMTP"
|
||||
return
|
||||
|
||||
|
||||
def getSchema():
|
||||
smtp_connection = v.Any(str, v.Schema({}, extra=True))
|
||||
return smtp_connection
|
||||
55
zuul/driver/smtp/smtpreporter.py
Normal file
55
zuul/driver/smtp/smtpreporter.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Copyright 2013 Rackspace Australia
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import voluptuous as v
|
||||
|
||||
from zuul.reporter import BaseReporter
|
||||
|
||||
|
||||
class SMTPReporter(BaseReporter):
|
||||
"""Sends off reports to emails via SMTP."""
|
||||
|
||||
name = 'smtp'
|
||||
log = logging.getLogger("zuul.reporter.smtp.Reporter")
|
||||
|
||||
def report(self, source, pipeline, item):
|
||||
"""Send the compiled report message via smtp."""
|
||||
message = self._formatItemReport(pipeline, item)
|
||||
|
||||
self.log.debug("Report change %s, params %s, message: %s" %
|
||||
(item.change, self.config, message))
|
||||
|
||||
from_email = self.config['from'] \
|
||||
if 'from' in self.config else None
|
||||
to_email = self.config['to'] \
|
||||
if 'to' in self.config else None
|
||||
|
||||
if 'subject' in self.config:
|
||||
subject = self.config['subject'].format(
|
||||
change=item.change)
|
||||
else:
|
||||
subject = "Report for change %s" % item.change
|
||||
|
||||
self.connection.sendMail(subject, message, from_email, to_email)
|
||||
|
||||
|
||||
def getSchema():
|
||||
smtp_reporter = v.Schema({
|
||||
'connection': str,
|
||||
'to': str,
|
||||
'from': str,
|
||||
'subject': str,
|
||||
})
|
||||
return smtp_reporter
|
||||
Reference in New Issue
Block a user