diff --git a/app.py b/app.py index c3b15d7aa..78243de75 100644 --- a/app.py +++ b/app.py @@ -32,6 +32,7 @@ from util.queuemetrics import QueueMetrics from util.config.provider import FileConfigProvider, TestConfigProvider from util.config.configutil import generate_secret_key from util.config.superusermanager import SuperUserManager +from buildman.jobutil.buildreporter import BuildMetrics OVERRIDE_CONFIG_DIRECTORY = 'conf/stack/' OVERRIDE_CONFIG_YAML_FILENAME = 'conf/stack/config.yaml' @@ -118,6 +119,7 @@ userevents = UserEventsBuilderModule(app) superusers = SuperUserManager(app) signer = Signer(app, OVERRIDE_CONFIG_DIRECTORY) queue_metrics = QueueMetrics(app) +build_metrics = BuildMetrics(app) tf = app.config['DB_TRANSACTION_FACTORY'] diff --git a/buildman/jobutil/buildreporter.py b/buildman/jobutil/buildreporter.py new file mode 100644 index 000000000..0016778dc --- /dev/null +++ b/buildman/jobutil/buildreporter.py @@ -0,0 +1,75 @@ +import logging +from trollius import From + +from util.cloudwatch import get_queue + + +# pylint: disable=invalid-name +logger = logging.getLogger(__name__) + + +# pylint: disable=too-few-public-methods +class BuildReporter(object): + """ + Base class for reporting build statuses to a metrics service. + """ + def report_completion_status(self, status): + """ + Method to invoke the recording of build's completion status to a metric service. + This must _not_ block because it is assumed to run in an event loop. + """ + raise NotImplementedError + + +class NullReporter(BuildReporter): + """ + The /dev/null of BuildReporters. + """ + def report_completion_status(self, *args): + pass + + +# pylint: disable=too-many-instance-attributes +class CloudWatchBuildReporter(BuildReporter): + """ + Implements a BuildReporter for Amazon's CloudWatch. + """ + # pylint: disable=too-many-arguments + def __init__(self, queue, namespace_name, completed_name, failed_name, incompleted_name): + self._queue = queue + self._namespace_name = namespace_name + self._completed_name = completed_name + self._failed_name = failed_name + self._incompleted_name = incompleted_name + + def report_completion_status(self, status): + if status == 'complete': + status_name = self._completed_name + elif status == 'error': + status_name = self._failed_name + elif status == 'incomplete': + status_name = self._incompleted_name + else: + return + + yield From(self._queue.put(self._namespace_name, status_name, 1, unit='Count')) + + +class BuildMetrics(object): + """ + BuildMetrics initializes a reporter for recording the status of build completions. + """ + def __init__(self, app=None): + self._app = app + if app is not None: + reporter_type = app.config.get('BUILD_METRICS_TYPE', 'Null') + if reporter_type == 'CloudWatch': + namespace = app.config.get('BUILD_METRICS_NAMESPACE') + completed_name = app.config.get('BUILD_METRICS_COMPLETED_NAME') + failed_name = app.config.get('BUILD_METRICS_FAILED_NAME') + incompleted_name = app.config.get('BUILD_METRICS_INCOMPLETED_NAME') + request_queue = get_queue(app) + self._reporter = CloudWatchBuildReporter(request_queue, namespace, completed_name, + failed_name, incompleted_name) + else: + self._reporter = NullReporter()