Merge StormCachingBuildFarm and BuildFarm.
authorJelmer Vernooij <jelmer@samba.org>
Sat, 4 Dec 2010 00:13:20 +0000 (01:13 +0100)
committerJelmer Vernooij <jelmer@samba.org>
Sat, 4 Dec 2010 00:13:20 +0000 (01:13 +0100)
buildfarm/__init__.py
buildfarm/build.py
buildfarm/sqldb.py
buildfarm/tests/__init__.py
buildfarm/tests/test_build.py
buildfarm/tests/test_buildfarm.py
buildfarm/tests/test_sqldb.py
buildfarm/web/__init__.py
builds.py
import-and-analyse.py
mail-dead-hosts.py

index c8fd125283d4211c57d3c94a78612fc4f78796c3..14134219f6dddf7c5d1362c48a1f147ffe31fac5 100644 (file)
@@ -17,7 +17,7 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-from buildfarm.sqldb import distinct_builds, Cast, StormBuild, setup_schema, StormCachingBuildResultStore, StormHostDatabase
+from buildfarm.sqldb import distinct_builds, Cast, StormBuild, setup_schema, StormHostDatabase
 from buildfarm.tree import Tree
 from storm.database import create_database
 from storm.expr import Desc
@@ -77,7 +77,8 @@ class BuildFarm(object):
 
     def _open_build_results(self):
         path = os.path.join(self.path, "data", "oldrevs")
-        return StormCachingBuildResultStore(path, self._get_store())
+        from buildfarm.build import BuildResultStore
+        return BuildResultStore(path, self._get_store())
 
     def _open_upload_build_results(self):
         from buildfarm.build import UploadBuildResultStore
index b218788ad44926c0de09fae68976047e34c011ab..0cbe3bf37b434e9469f17ac7a3e364563d81f061 100644 (file)
@@ -26,6 +26,9 @@ import collections
 import hashlib
 import os
 import re
+from storm.locals import Int, RawStr
+from storm.store import Store
+from storm.expr import Desc
 import time
 
 
@@ -384,34 +387,74 @@ class UploadBuildResultStore(object):
         return Build(basename, tree, host, compiler)
 
 
+class StormBuild(Build):
+    __storm_table__ = "build"
+
+    id = Int(primary=True)
+    tree = RawStr()
+    revision = RawStr()
+    host = RawStr()
+    compiler = RawStr()
+    checksum = RawStr()
+    upload_time = Int(name="age")
+    status_str = RawStr(name="status")
+    basename = RawStr()
+    host_id = Int()
+    tree_id = Int()
+    compiler_id = Int()
+
+    def status(self):
+        return BuildStatus.__deserialize__(self.status_str)
+
+    def revision_details(self):
+        return self.revision
+
+    def log_checksum(self):
+        return self.checksum
+
+    def remove(self):
+        super(StormBuild, self).remove()
+        Store.of(self).remove(self)
+
+    def remove_logs(self):
+        super(StormBuild, self).remove_logs()
+        self.basename = None
+
+
 class BuildResultStore(object):
     """The build farm build result database."""
 
-    def __init__(self, path):
-        """Open the database.
+    def __init__(self, basedir, store=None):
+        from buildfarm.sqldb import memory_store
+        if store is None:
+            store = memory_store()
 
-        :param path: Build result base directory
-        """
-        self.path = path
+        self.store = store
+        self.path = basedir
 
     def __contains__(self, build):
         try:
-            if build.revision:
-                rev = build.revision
-            else:
-                rev = build.revision_details()
-            self.get_build(build.tree, build.host, build.compiler, rev)
+            self.get_by_checksum(build.log_checksum())
+            return True
         except NoSuchBuildError:
             return False
-        else:
-            return True
 
-    def get_build(self, tree, host, compiler, rev, checksum=None):
-        basename = self.build_fname(tree, host, compiler, rev)
-        logf = "%s.log" % basename
-        if not os.path.exists(logf):
-            raise NoSuchBuildError(tree, host, compiler, rev)
-        return Build(basename, tree, host, compiler, rev)
+    def get_build(self, tree, host, compiler, revision=None, checksum=None):
+        from buildfarm.sqldb import Cast
+        expr = [
+            Cast(StormBuild.tree, "TEXT") == Cast(tree, "TEXT"),
+            Cast(StormBuild.host, "TEXT") == Cast(host, "TEXT"),
+            Cast(StormBuild.compiler, "TEXT") == Cast(compiler, "TEXT"),
+            ]
+        if revision is not None:
+            expr.append(Cast(StormBuild.revision, "TEXT") == Cast(revision, "TEXT"))
+        if checksum is not None:
+            expr.append(Cast(StormBuild.checksum, "TEXT") == Cast(checksum, "TEXT"))
+        result = self.store.find(StormBuild, *expr).order_by(Desc(StormBuild.upload_time))
+        ret = result.first()
+        if ret is None:
+            raise NoSuchBuildError(tree, host, compiler, revision)
+        return ret
 
     def build_fname(self, tree, host, compiler, rev):
         """get the name of the build file"""
@@ -433,15 +476,24 @@ class BuildResultStore(object):
             yield self.get_build(tree, host, compiler, rev)
 
     def get_old_builds(self, tree, host, compiler):
-        """get a list of old builds and their status."""
-        ret = []
-        for build in self.get_all_builds():
-            if build.tree == tree and build.host == host and build.compiler == compiler:
-                ret.append(build)
-        ret.sort(lambda a, b: cmp(a.upload_time, b.upload_time))
-        return ret
+        result = self.store.find(StormBuild,
+            StormBuild.tree == tree,
+            StormBuild.host == host,
+            StormBuild.compiler == compiler)
+        return result.order_by(Desc(StormBuild.upload_time))
 
     def upload_build(self, build):
+        from buildfarm.sqldb import Cast, StormHost
+        try:
+            existing_build = self.get_by_checksum(build.log_checksum())
+        except NoSuchBuildError:
+            pass
+        else:
+            # Already present
+            assert build.tree == existing_build.tree
+            assert build.host == existing_build.host
+            assert build.compiler == existing_build.compiler
+            return existing_build
         rev = build.revision_details()
 
         new_basename = self.build_fname(build.tree, build.host, build.compiler, rev)
@@ -457,10 +509,53 @@ class BuildResultStore(object):
         os.link(build.basename+".log", new_basename+".log")
         if os.path.exists(build.basename+".err"):
             os.link(build.basename+".err", new_basename+".err")
-        return Build(new_basename, build.tree, build.host, build.compiler, rev)
+        new_basename = self.build_fname(build.tree, build.host, build.compiler,
+                rev)
+        new_build = StormBuild(new_basename, build.tree, build.host,
+            build.compiler, rev)
+        new_build.checksum = build.log_checksum()
+        new_build.upload_time = build.upload_time
+        new_build.status_str = build.status().__serialize__()
+        new_build.basename = new_basename
+        host = self.store.find(StormHost,
+            Cast(StormHost.name, "TEXT") == Cast(build.host, "TEXT")).one()
+        assert host is not None, "Unable to find host %r" % build.host
+        new_build.host_id = host.id
+        self.store.add(new_build)
+        return new_build
+
+    def get_by_checksum(self, checksum):
+        from buildfarm.sqldb import Cast
+        result = self.store.find(StormBuild,
+            Cast(StormBuild.checksum, "TEXT") == checksum)
+        ret = result.one()
+        if ret is None:
+            raise NoSuchBuildError(None, None, None, None)
+        return ret
 
     def get_previous_revision(self, tree, host, compiler, revision):
-        raise NoSuchBuildError(tree, host, compiler, revision)
+        from buildfarm.sqldb import Cast
+        cur_build = self.get_build(tree, host, compiler, revision)
+
+        result = self.store.find(StormBuild,
+            Cast(StormBuild.tree, "TEXT") == Cast(tree, "TEXT"),
+            Cast(StormBuild.host, "TEXT") == Cast(host, "TEXT"),
+            Cast(StormBuild.compiler, "TEXT") == Cast(compiler, "TEXT"),
+            Cast(StormBuild.revision, "TEXT") != Cast(revision, "TEXT"),
+            StormBuild.id < cur_build.id)
+        result = result.order_by(Desc(StormBuild.id))
+        prev_build = result.first()
+        if prev_build is None:
+            raise NoSuchBuildError(tree, host, compiler, revision)
+        return prev_build.revision
 
     def get_latest_revision(self, tree, host, compiler):
-        raise NoSuchBuildError(tree, host, compiler)
+        result = self.store.find(StormBuild,
+            StormBuild.tree == tree,
+            StormBuild.host == host,
+            StormBuild.compiler == compiler)
+        result = result.order_by(Desc(StormBuild.id))
+        build = result.first()
+        if build is None:
+            raise NoSuchBuildError(tree, host, compiler)
+        return build.revision
index 2ef6f00a94b3ba2cb502871fe2bf5ddf8ebab926..f679fb40159151951460787561355882f5e29bcc 100644 (file)
@@ -21,10 +21,7 @@ from buildfarm.tree import (
     Tree,
     )
 from buildfarm.build import (
-    Build,
-    BuildResultStore,
-    BuildStatus,
-    NoSuchBuildError,
+    StormBuild,
     Test,
     TestResult,
     )
@@ -35,7 +32,7 @@ from buildfarm.hostdb import (
     NoSuchHost,
     )
 
-import os
+
 try:
     from pysqlite2 import dbapi2 as sqlite3
 except ImportError:
@@ -62,40 +59,6 @@ def compile_count(compile, cast, state):
     return "CAST(%s AS %s)" % (column, cast.type)
 
 
-class StormBuild(Build):
-    __storm_table__ = "build"
-
-    id = Int(primary=True)
-    tree = RawStr()
-    revision = RawStr()
-    host = RawStr()
-    compiler = RawStr()
-    checksum = RawStr()
-    upload_time = Int(name="age")
-    status_str = RawStr(name="status")
-    basename = RawStr()
-    host_id = Int()
-    tree_id = Int()
-    compiler_id = Int()
-
-    def status(self):
-        return BuildStatus.__deserialize__(self.status_str)
-
-    def revision_details(self):
-        return self.revision
-
-    def log_checksum(self):
-        return self.checksum
-
-    def remove(self):
-        super(StormBuild, self).remove()
-        Store.of(self).remove(self)
-
-    def remove_logs(self):
-        super(StormBuild, self).remove_logs()
-        self.basename = None
-
-
 class StormHost(Host):
     __storm_table__ = "host"
 
@@ -167,109 +130,6 @@ class StormHostDatabase(HostDatabase):
         self.store.commit()
 
 
-class StormCachingBuildResultStore(BuildResultStore):
-
-    def __init__(self, basedir, store=None):
-        super(StormCachingBuildResultStore, self).__init__(basedir)
-
-        if store is None:
-            store = memory_store()
-
-        self.store = store
-
-    def get_by_checksum(self, checksum):
-        result = self.store.find(StormBuild,
-            Cast(StormBuild.checksum, "TEXT") == checksum)
-        ret = result.one()
-        if ret is None:
-            raise NoSuchBuildError(None, None, None, None)
-        return ret
-
-    def __contains__(self, build):
-        try:
-            self.get_by_checksum(build.log_checksum())
-            return True
-        except NoSuchBuildError:
-            return False
-
-    def get_previous_revision(self, tree, host, compiler, revision):
-        cur_build = self.get_build(tree, host, compiler, revision)
-
-        result = self.store.find(StormBuild,
-            Cast(StormBuild.tree, "TEXT") == Cast(tree, "TEXT"),
-            Cast(StormBuild.host, "TEXT") == Cast(host, "TEXT"),
-            Cast(StormBuild.compiler, "TEXT") == Cast(compiler, "TEXT"),
-            Cast(StormBuild.revision, "TEXT") != Cast(revision, "TEXT"),
-            StormBuild.id < cur_build.id)
-        result = result.order_by(Desc(StormBuild.id))
-        prev_build = result.first()
-        if prev_build is None:
-            raise NoSuchBuildError(tree, host, compiler, revision)
-        return prev_build.revision
-
-    def get_latest_revision(self, tree, host, compiler):
-        result = self.store.find(StormBuild,
-            StormBuild.tree == tree,
-            StormBuild.host == host,
-            StormBuild.compiler == compiler)
-        result = result.order_by(Desc(StormBuild.id))
-        build = result.first()
-        if build is None:
-            raise NoSuchBuildError(tree, host, compiler)
-        return build.revision
-
-    def upload_build(self, build):
-        try:
-            existing_build = self.get_by_checksum(build.log_checksum())
-        except NoSuchBuildError:
-            pass
-        else:
-            # Already present
-            assert build.tree == existing_build.tree
-            assert build.host == existing_build.host
-            assert build.compiler == existing_build.compiler
-            return existing_build
-        rev = build.revision_details()
-        super(StormCachingBuildResultStore, self).upload_build(build)
-        new_basename = self.build_fname(build.tree, build.host, build.compiler,
-                rev)
-        new_build = StormBuild(new_basename, build.tree, build.host,
-            build.compiler, rev)
-        new_build.checksum = build.log_checksum()
-        new_build.upload_time = build.upload_time
-        new_build.status_str = build.status().__serialize__()
-        new_build.basename = new_basename
-        host = self.store.find(StormHost,
-            Cast(StormHost.name, "TEXT") == Cast(build.host, "TEXT")).one()
-        assert host is not None, "Unable to find host %r" % build.host
-        new_build.host_id = host.id
-        self.store.add(new_build)
-        return new_build
-
-    def get_old_builds(self, tree, host, compiler):
-        result = self.store.find(StormBuild,
-            StormBuild.tree == tree,
-            StormBuild.host == host,
-            StormBuild.compiler == compiler)
-        return result.order_by(Desc(StormBuild.upload_time))
-
-    def get_build(self, tree, host, compiler, revision=None, checksum=None):
-        expr = [
-            Cast(StormBuild.tree, "TEXT") == Cast(tree, "TEXT"),
-            Cast(StormBuild.host, "TEXT") == Cast(host, "TEXT"),
-            Cast(StormBuild.compiler, "TEXT") == Cast(compiler, "TEXT"),
-            ]
-        if revision is not None:
-            expr.append(Cast(StormBuild.revision, "TEXT") == Cast(revision, "TEXT"))
-        if checksum is not None:
-            expr.append(Cast(StormBuild.checksum, "TEXT") == Cast(checksum, "TEXT"))
-        result = self.store.find(StormBuild, *expr).order_by(Desc(StormBuild.upload_time))
-        ret = result.first()
-        if ret is None:
-            raise NoSuchBuildError(tree, host, compiler, revision)
-        return ret
-
-
 def distinct_builds(builds):
     done = set()
     for build in builds:
index 6cbad6416a36cd297f4acfa373de18dd2785eb49..bc66ab1b726935bc50c2e46c1e3d9e5474cee563 100644 (file)
@@ -71,12 +71,8 @@ class BuildFarmTestCase(TestCase):
             f.close()
 
     def write_hosts(self, hosts):
-        f = open(os.path.join(self.path, "web", "hosts.list"), "w")
-        try:
-            for name, platform in hosts.iteritems():
-                f.write("%s: %s\n" % (name, platform))
-        finally:
-            f.close()
+        for host in hosts:
+            self.buildfarm.hostdb.createhost(host)
 
     def write_trees(self, trees):
         f = open(os.path.join(self.path, "web", "trees.conf"), "w")
index 35638653346047540060c5ad06e378ba6453a151..4c2d0f0007ad2ec9b60b98de50d32dbde0c664f7 100755 (executable)
@@ -33,19 +33,35 @@ from buildfarm import BuildFarm
 from buildfarm.tests import BuildFarmTestCase
 
 
-class NonexistantTests(unittest.TestCase):
-
-    def test_nonexistant(self):
-        self.assertRaises(
-            Exception, BuildResultStore, "somedirthatdoesn'texist", None)
-
-
-class BuildResultStoreTestBase(object):
+class BuildResultStoreTests(BuildFarmTestCase):
 
     def setUp(self):
+        super(BuildResultStoreTests, self).setUp()
+        self.buildfarm = BuildFarm(self.path)
         self.write_compilers(["cc", "gcc"])
         self.write_hosts({"charis": "Some machine",
                           "myhost": "Another host"})
+        self.x = self.buildfarm.builds
+
+    def test_get_previous_revision_result(self):
+        path = self.create_mock_logfile("tdb", "charis", "cc", contents="""
+BUILD COMMIT REVISION: myrev
+""")
+        self.x.upload_build(Build(path[:-4], "tdb", "charis", "cc"))
+        path = self.create_mock_logfile("tdb", "charis", "cc", contents="""
+BUILD COMMIT REVISION: myotherrev
+""")
+        self.x.upload_build(Build(path[:-4], "tdb", "charis", "cc"))
+        self.assertRaises(NoSuchBuildError, self.x.get_previous_revision, "tdb", "charis", "cc", "unknown")
+        self.assertRaises(NoSuchBuildError, self.x.get_previous_revision, "tdb", "charis", "cc", "myrev")
+        self.assertEquals("myrev", self.x.get_previous_revision("tdb", "charis", "cc", "myotherrev"))
+
+    def test_get_latest_revision(self):
+        path = self.create_mock_logfile("tdb", "charis", "cc", "22", contents="""
+BUILD COMMIT REVISION: myrev
+""")
+        self.x.upload_build(Build(path[:-4], "tdb", "charis", "cc"))
+        self.assertEquals("myrev", self.x.get_latest_revision("tdb", "charis", "cc"))
 
     def test_build_fname(self):
         self.assertEquals(
index 36ff66fe3885124a594cc733c04446165a7c58b7..d78ed6869fb27c13eeab9aff29ac261aeb750211 100644 (file)
@@ -75,11 +75,13 @@ class BuildFarmTests(BuildFarmTestCase):
 
     def setUp(self):
         super(BuildFarmTests, self).setUp()
+        self.buildfarm = BuildFarm(self.path)
         self.write_compilers(["cc"])
         self.write_hosts({"myhost": "Fedora",
                           "charis": "Debian"})
         self.write_trees({"trivial": {"scm": "git", "repo": "git://foo", "branch": "master"},
                           "other": {"scm": "git", "repo": "other.git", "branch": "HEAD"}})
+        self.buildfarm.commit()
         self.x = BuildFarm(self.path)
 
     def test_get_new_builds_empty(self):
index 684d912f707f3dd6d078e271e2f97c79b9ad8bd0..3805e588293e0b6463417db00a4f62c89bad5099 100644 (file)
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+from buildfarm import BuildFarm
 from buildfarm.build import (
     Build,
     NoSuchBuildError,
     )
 from buildfarm.tests import BuildFarmTestCase
-from buildfarm.tests.test_build import BuildResultStoreTestBase
 from buildfarm.tests.test_hostdb import HostDatabaseTests
 from buildfarm.sqldb import (
     StormHostDatabase,
@@ -36,29 +36,4 @@ class StormHostDatabaseTests(testtools.TestCase, HostDatabaseTests):
         self.db = StormHostDatabase()
 
 
-class StormCachingBuildResultStoreTests(BuildFarmTestCase,BuildResultStoreTestBase):
 
-    def setUp(self):
-        BuildFarmTestCase.setUp(self)
-        BuildResultStoreTestBase.setUp(self)
-        self.x = self.buildfarm.builds
-
-    def test_get_previous_revision_result(self):
-        path = self.create_mock_logfile("tdb", "charis", "cc", contents="""
-BUILD COMMIT REVISION: myrev
-""")
-        self.x.upload_build(Build(path[:-4], "tdb", "charis", "cc"))
-        path = self.create_mock_logfile("tdb", "charis", "cc", contents="""
-BUILD COMMIT REVISION: myotherrev
-""")
-        self.x.upload_build(Build(path[:-4], "tdb", "charis", "cc"))
-        self.assertRaises(NoSuchBuildError, self.x.get_previous_revision, "tdb", "charis", "cc", "unknown")
-        self.assertRaises(NoSuchBuildError, self.x.get_previous_revision, "tdb", "charis", "cc", "myrev")
-        self.assertEquals("myrev", self.x.get_previous_revision("tdb", "charis", "cc", "myotherrev"))
-
-    def test_get_latest_revision(self):
-        path = self.create_mock_logfile("tdb", "charis", "cc", "22", contents="""
-BUILD COMMIT REVISION: myrev
-""")
-        self.x.upload_build(Build(path[:-4], "tdb", "charis", "cc"))
-        self.assertEquals("myrev", self.x.get_latest_revision("tdb", "charis", "cc"))
index 17d5a556e2b66e6254cc2673fd16a977cdfaebf6..bd66aa07705df2364580fb8736250dda0a9d174f 100755 (executable)
@@ -973,7 +973,7 @@ if __name__ == '__main__':
     parser.add_option("--port", help="Port to listen on [localhost:8000]",
         default="localhost:8000", type=str)
     opts, args = parser.parse_args()
-    from buildfarm.sqldb import BuildFarm
+    from buildfarm import BuildFarm
     buildfarm = BuildFarm()
     buildApp = BuildFarmApp(buildfarm)
     from wsgiref.simple_server import make_server
index 8f2105142819b53850690ee2e1e6a02966f774a6..a1f3fc929ffb27016bf75eed9e66ee5e424a9bfe 100755 (executable)
--- a/builds.py
+++ b/builds.py
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-from buildfarm.sqldb import BuildFarm
+from buildfarm import BuildFarm
 import optparse
 import sys
 
index 6202f7f00b97ae6dcca716e6956d95265d6ed7dd..921e156f404eb07b5e5e68b083c3ad2343a6c325 100755 (executable)
@@ -14,7 +14,7 @@ from buildfarm.build import (
     MissingRevisionInfo,
     NoSuchBuildError,
     )
-from buildfarm.sqldb import BuildFarm
+from buildfarm import BuildFarm
 from buildfarm.web import build_uri
 from email.mime.text import MIMEText
 import logging
index f4b13fdeb92b3077809577f5bd4a84082b0a58b2..149991276a0eec72bffaa3ea7b845f724bf27ce4 100755 (executable)
@@ -17,7 +17,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-from buildfarm.sqldb import BuildFarm
+from buildfarm import BuildFarm
 from buildfarm.web import host_uri
 import optparse
 import smtplib