[ews-build] Configure buildbot to send events to ews-app
authoraakash_jain@apple.com <aakash_jain@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 Jan 2019 20:37:02 +0000 (20:37 +0000)
committeraakash_jain@apple.com <aakash_jain@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 Jan 2019 20:37:02 +0000 (20:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193968

Reviewed by Lucas Forschler.

* BuildSlaveSupport/ews-build/events.py: Added.
* BuildSlaveSupport/ews-build/master.cfg:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@240671 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/BuildSlaveSupport/ews-build/events.py [new file with mode: 0644]
Tools/BuildSlaveSupport/ews-build/master.cfg
Tools/ChangeLog

diff --git a/Tools/BuildSlaveSupport/ews-build/events.py b/Tools/BuildSlaveSupport/ews-build/events.py
new file mode 100644 (file)
index 0000000..2b94ed0
--- /dev/null
@@ -0,0 +1,195 @@
+# Copyright (C) 2019 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import datetime
+import json
+import time
+
+from buildbot.util import service
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.internet.defer import succeed
+from twisted.python import log
+from twisted.web.client import Agent
+from twisted.web.http_headers import Headers
+from twisted.web.iweb import IBodyProducer
+from zope.interface import implements
+
+
+class JSONProducer(object):
+    """
+    Perform JSON asynchronously as to not lock the buildbot main event loop
+    """
+    implements(IBodyProducer)
+
+    def __init__(self, data):
+        try:
+            self.body = json.dumps(data, default=self.json_serialize_datetime)
+        except TypeError:
+            self.body = ''
+        self.length = len(self.body)
+
+    def startProducing(self, consumer):
+        if self.body:
+            consumer.write(self.body)
+        return succeed(None)
+
+    def pauseProducing(self):
+        pass
+
+    def stopProducing(self):
+        pass
+
+    def json_serialize_datetime(self, obj):
+        """
+        Serializing buildbot dates into UNIX epoch timestamps.
+        """
+        if isinstance(obj, datetime.datetime):
+            return int(time.mktime(obj.timetuple()))
+
+        raise TypeError("Type %s not serializable" % type(obj))
+
+
+class Events(service.BuildbotService):
+
+    EVENT_SERVER_ENDPOINT = 'http://ews.webkit-uat.org/results/'
+
+    def __init__(self, type_prefix='', name='Events'):
+        """
+        Initialize the Events Plugin. Sends data to event server on specific buildbot events.
+        :param type_prefix: [optional] prefix we want to add to the 'type' field on the json we send
+         to event server. (i.e. ews-build, where 'ews-' is the prefix.
+        :return: Events Object
+        """
+        service.BuildbotService.__init__(self, name=name)
+
+        if type_prefix and not type_prefix.endswith("-"):
+            type_prefix += "-"
+        self.type_prefix = type_prefix
+
+    def sendData(self, data):
+        agent = Agent(reactor)
+        body = JSONProducer(data)
+
+        agent.request('POST', self.EVENT_SERVER_ENDPOINT, Headers({'Content-Type': ['application/json']}), body)
+
+    def getBuilderName(self, build):
+        if not (build and 'properties' in build):
+            return ''
+
+        return build.get('properties').get('buildername')[0]
+
+    def getPatchID(self, build):
+        if not (build and 'properties' in build):
+            return None
+
+        return build.get('properties').get('patch_id')[0]
+
+    @defer.inlineCallbacks
+    def buildStarted(self, key, build):
+        if not build.get('properties'):
+            build['properties'] = yield self.master.db.builds.getBuildProperties(build.get('buildid'))
+
+        data = {
+            "type": self.type_prefix + "build",
+            "status": "started",
+            "patch_id": self.getPatchID(build),
+            "build_id": build.get('buildid'),
+            "builder_id": build.get('builderid'),
+            "number": build.get('number'),
+            "result": build.get('results'),
+            "started_at": build.get('started_at'),
+            "complete_at": build.get('complete_at'),
+            "state_string": build.get('state_string'),
+            "buildername": self.getBuilderName(build),
+        }
+
+        self.sendData(data)
+
+    @defer.inlineCallbacks
+    def buildFinished(self, key, build):
+        if not build.get('properties'):
+            build['properties'] = yield self.master.db.builds.getBuildProperties(build.get('buildid'))
+        if not build.get('steps'):
+            build['steps'] = yield self.master.db.steps.getSteps(build.get('buildid'))
+
+        data = {
+            "type": self.type_prefix + "build",
+            "status": "finished",
+            "patch_id": self.getPatchID(build),
+            "build_id": build.get('buildid'),
+            "builder_id": build.get('builderid'),
+            "number": build.get('number'),
+            "result": build.get('results'),
+            "started_at": build.get('started_at'),
+            "complete_at": build.get('complete_at'),
+            "state_string": build.get('state_string'),
+            "buildername": self.getBuilderName(build),
+            "steps": build.get('steps'),
+        }
+
+        self.sendData(data)
+
+    def stepStarted(self, key, step):
+        data = {
+            "type": self.type_prefix + "step",
+            "status": "started",
+            "step_id": step.get('stepid'),
+            "build_id": step.get('buildid'),
+            "result": step.get('results'),
+            "state_string": step.get('state_string'),
+            "started_at": step.get('started_at'),
+            "complete_at": step.get('complete_at'),
+        }
+
+        self.sendData(data)
+
+    def stepFinished(self, key, step):
+        data = {
+            "type": self.type_prefix + "step",
+            "status": "finished",
+            "step_id": step.get('stepid'),
+            "build_id": step.get('buildid'),
+            "result": step.get('results'),
+            "state_string": step.get('state_string'),
+            "started_at": step.get('started_at'),
+            "complete_at": step.get('complete_at'),
+        }
+
+        self.sendData(data)
+
+    @defer.inlineCallbacks
+    def startService(self):
+        yield service.BuildbotService.startService(self)
+
+        startConsuming = self.master.mq.startConsuming
+
+        self._buildStartedConsumer = yield startConsuming(self.buildStarted, ('builds', None, 'new'))
+        self._buildCompleteConsumer = yield startConsuming(self.buildFinished, ('builds', None, 'finished'))
+        self._stepStartedConsumer = yield startConsuming(self.stepStarted, ('steps', None, 'started'))
+        self._stepFinishedConsumer = yield startConsuming(self.stepFinished, ('steps', None, 'finished'))
+
+    def stopService(self):
+        self._buildStartedConsumer.stopConsuming()
+        self._buildCompleteConsumer.stopConsuming()
+        self._stepStartedConsumer.stopConsuming()
+        self._stepFinishedConsumer.stopConsuming()
index b7a1cde..5ec0d03 100644 (file)
@@ -1,7 +1,7 @@
 import os
 
 import loadConfig
-
+from events import Events
 
 is_test_mode_enabled = os.getenv('BUILDBOT_TESTING') is not None
 
@@ -25,3 +25,6 @@ c['buildbotURL'] = 'https://ews-build.webkit.org/'
 c['buildbotNetUsageData'] = None
 
 loadConfig.loadBuilderConfig(c, use_localhost_worker=is_test_mode_enabled)
+
+event_reporter = Events(type_prefix='ews')
+c['services'] = [event_reporter]
index 597dd34..fb465c8 100644 (file)
@@ -1,3 +1,13 @@
+2019-01-29  Aakash Jain  <aakash_jain@apple.com>
+
+        [ews-build] Configure buildbot to send events to ews-app
+        https://bugs.webkit.org/show_bug.cgi?id=193968
+
+        Reviewed by Lucas Forschler.
+
+        * BuildSlaveSupport/ews-build/events.py: Added.
+        * BuildSlaveSupport/ews-build/master.cfg:
+
 2019-01-29  Keith Rollin  <krollin@apple.com>
 
         Add .xcfilelists to Run Script build phases