Skip to content
Browse files

Bug 508403 - do clobbers on release builds. r=nthomas

  • Loading branch information...
1 parent 1763ed8 commit 82b7cea1299d702163e892558cd7aaab0fa9f9c6 @catlee catlee committed
Showing with 782 additions and 109 deletions.
  1. +111 −68 clobberer/clobberer.py
  2. +314 −41 clobberer/index.php
  3. +357 −0 clobberer/test_clobberer.py
View
179 clobberer/clobberer.py
@@ -1,19 +1,18 @@
-import sys, shutil, urllib2, urllib, os
-from datetime import datetime, timedelta
+#!/usr/bin/python
+# vim:sts=2 sw=2
+import sys, shutil, urllib2, urllib, os, traceback, time
clobber_suffix='.deleteme'
-def str_to_datetime(s):
- return datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
-
-def datetime_to_str(dt):
- return dt.strftime("%Y-%m-%d %H:%M:%S")
+def ts_to_str(ts):
+ if ts is None:
+ return None
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))
-def write_file(dt, fn):
- assert isinstance(dt, datetime)
- dt = datetime_to_str(dt)
+def write_file(ts, fn):
+ assert isinstance(ts, int)
f = open(fn, "w")
- f.write(dt)
+ f.write(str(ts))
f.close()
def read_file(fn):
@@ -22,7 +21,7 @@ def read_file(fn):
data = open(fn).read().strip()
try:
- return str_to_datetime(data)
+ return int(data)
except ValueError:
return None
@@ -89,88 +88,132 @@ def do_clobber(dir, dryrun=False, skip=None):
# Prevent repeated moving.
if f.endswith(clobber_suffix):
rmdirRecursive(f)
- else:
+ else:
shutil.move(f, clobber_path)
rmdirRecursive(clobber_path)
except:
print "Couldn't clobber properly, bailing out."
sys.exit(1)
-def getClobberDate(baseURL, branch, builder, slave):
- url = "%s?%s" % (baseURL,
- urllib.urlencode(dict(branch=branch, builder=builder, slave=slave)))
+def getClobberDates(clobberURL, branch, buildername, builddir, slave, master):
+ params = dict(branch=branch, buildername=buildername, builddir=builddir, slave=slave, master=master)
+ url = "%s?%s" % (clobberURL, urllib.urlencode(params))
print "Checking clobber URL: %s" % url
data = urllib2.urlopen(url).read().strip()
+
+ retval = {}
try:
- return str_to_datetime(data)
+ for line in data.split("\n"):
+ line = line.strip()
+ if not line:
+ continue
+ builddir, builder_time, who = line.split(":")
+ builder_time = int(builder_time)
+ retval[builddir] = (builder_time, who)
+ return retval
except ValueError:
- return None
+ print "Error parsing response from server"
+ print data
+ raise
if __name__ == "__main__":
from optparse import OptionParser
- parser = OptionParser()
+ parser = OptionParser("%prog [options] clobberURL branch buildername builddir slave master")
parser.add_option("-n", "--dry-run", dest="dryrun", action="store_true",
default=False, help="don't actually delete anything")
parser.add_option("-t", "--periodic", dest="period", type="float",
- default=24*7, help="hours between periodic clobbers")
- parser.add_option('-s', '--skip', help='do not delete this directory',
+ default=None, help="hours between periodic clobbers")
+ parser.add_option('-s', '--skip', help='do not delete this file/directory',
action='append', dest='skip', default=['last-clobber'])
parser.add_option('-d', '--dir', help='clobber this directory',
dest='dir', default='.', type='string')
+ parser.add_option('-v', '--verbose', help='be more verbose',
+ dest='verbose', action='store_true', default=False)
options, args = parser.parse_args()
- periodicClobberTime = timedelta(hours = options.period)
+ if len(args) != 6:
+ parser.error("Incorrect number of arguments")
- baseURL, branch, builder, slave = args
+ if options.period:
+ periodicClobberTime = options.period * 3600
+ else:
+ periodicClobberTime = None
+
+ clobberURL, branch, builder, my_builddir, slave, master = args
try:
- server_clobber_date = getClobberDate(baseURL, branch, builder, slave)
+ server_clobber_dates = getClobberDates(clobberURL, branch, builder, my_builddir, slave, master)
except:
+ if options.verbose:
+ traceback.print_exc()
print "Error contacting server"
sys.exit(1)
- our_clobber_date = read_file("last-clobber")
-
- clobber = False
-
- print "Our last clobber date: ", our_clobber_date
- print "Server clobber date: ", server_clobber_date
-
- # If we don't have a last clobber date, then this is probably a fresh build.
- # We should only do a forced server clobber if we know when our last clobber
- # was, and if the server date is more recent than that.
- if server_clobber_date is not None and our_clobber_date is not None:
- # If the server is giving us a clobber date, compare the server's idea of
- # the clobber date to our last clobber date
- if server_clobber_date > our_clobber_date:
- # If the server's clobber date is greater than our last clobber date,
- # then we should clobber.
- clobber = True
- # We should also update our clobber date to match the server's
- our_clobber_date = server_clobber_date
- print "Server is forcing a clobber"
-
- if not clobber:
- # Next, check if more than the periodicClobberTime period has passed since
- # our last clobber
- if our_clobber_date is None:
- # We've never been clobbered
- # Set our last clobber time to now, so that we'll clobber
- # properly after periodicClobberTime
- our_clobber_date = datetime.utcnow()
+ if options.verbose:
+ print "Server gave us", server_clobber_dates
+
+ now = int(time.time())
+
+ # Add ourself to the server_clobber_dates if it's not set
+ # This happens when this slave has never been clobbered
+ if my_builddir not in server_clobber_dates:
+ server_clobber_dates[my_builddir] = None, ""
+
+ root_dir = os.path.abspath(options.dir)
+
+ for builddir, (server_clobber_date, who) in server_clobber_dates.items():
+ builder_dir = os.path.join(root_dir, builddir)
+ if not os.path.isdir(builder_dir):
+ print "%s doesn't exist, skipping" % builder_dir
+ continue
+ os.chdir(builder_dir)
+
+ our_clobber_date = read_file("last-clobber")
+
+ clobber = False
+
+ print "%s:Our last clobber date: " % builddir, ts_to_str(our_clobber_date)
+ print "%s:Server clobber date: " % builddir, ts_to_str(server_clobber_date)
+
+ # If we don't have a last clobber date, then this is probably a fresh build.
+ # We should only do a forced server clobber if we know when our last clobber
+ # was, and if the server date is more recent than that.
+ if server_clobber_date is not None and our_clobber_date is not None:
+ # If the server is giving us a clobber date, compare the server's idea of
+ # the clobber date to our last clobber date
+ if server_clobber_date > our_clobber_date:
+ # If the server's clobber date is greater than our last clobber date,
+ # then we should clobber.
+ clobber = True
+ # We should also update our clobber date to match the server's
+ our_clobber_date = server_clobber_date
+ if who:
+ print "%s:Server is forcing a clobber, initiated by %s" % (builddir, who)
+ else:
+ print "%s:Server is forcing a clobber" % builddir
+
+ if not clobber:
+ # Disable periodic clobbers for builders that aren't my_builddir
+ if builddir != my_builddir:
+ continue
+
+ # Next, check if more than the periodicClobberTime period has passed since
+ # our last clobber
+ if our_clobber_date is None:
+ # We've never been clobbered
+ # Set our last clobber time to now, so that we'll clobber
+ # properly after periodicClobberTime
+ our_clobber_date = now
+ write_file(our_clobber_date, "last-clobber")
+ elif periodicClobberTime and now > our_clobber_date + periodicClobberTime:
+ # periodicClobberTime has passed since our last clobber
+ clobber = True
+ # Update our clobber date to now
+ our_clobber_date = now
+ print "%s:More than %s seconds have passed since our last clobber" % (builddir, periodicClobberTime)
+
+ if clobber:
+ # Finally, perform a clobber if we're supposed to
+ print "%s:Clobbering..." % builddir
+ do_clobber(builder_dir, options.dryrun, options.skip)
write_file(our_clobber_date, "last-clobber")
- elif datetime.utcnow() > our_clobber_date + periodicClobberTime:
- # periodicClobberTime has passed since our last clobber
- clobber = True
- # Update our clobber date to now
- our_clobber_date = datetime.utcnow()
- print "More than %s have passed since our last clobber" % periodicClobberTime
-
- if clobber:
- # Finally, perform a clobber if we're supposed to
- if os.path.exists(options.dir):
- print "Clobbering..."
- do_clobber(options.dir, options.dryrun, options.skip)
- else:
- print "Clobber failed because '%s' doesn't exist" % options.dir
- write_file(our_clobber_date, "last-clobber")
View
355 clobberer/index.php
@@ -8,8 +8,51 @@
http://hg.mozilla.org/build/buildbotcustom/file/default/process/factory.py
*/
-$CLOBBERER_DB = '/var/www/html/build/stage-clobberer/db/clobberer.db';
-//$CLOBBERER_DB = '/var/www/html/build/clobberer/clobberer.db';
+$CLOBBERER_DB = 'db/clobberer.db';
+
+$RELEASE_BUILDERS = array(
+ 'linux_build',
+ 'macosx_build',
+ 'win32_build',
+ 'wince_build',
+
+ 'linux_repack',
+ 'macosx_repack',
+ 'win32_repack',
+ 'wince_repack',
+
+ 'linux_update_verify',
+ 'macosx_update_verify',
+ 'win32_update_verify',
+ 'wince_update_verify',
+
+ 'release-linux-unittest-mochitests',
+ 'release-linux-unittest-everythingelse',
+ 'release-win32-unittest-mochitests',
+ 'release-win32-unittest-everythingelse',
+ 'release-macosx-unittest-mochitests',
+ 'release-macosx-unittest-everythingelse',
+
+ 'final_verification',
+ 'l10n_verification',
+ 'tag',
+ 'source',
+ 'updates',
+);
+
+// TODO: Figure out if we can use LDAP to do this
+$SPECIAL_PEOPLE = array(
+ 'anodelman@mozilla.com',
+ 'armenzg@mozilla.com',
+ 'asasaki@mozilla.com',
+ 'bhearsum@mozilla.com',
+ 'catlee@mozilla.com',
+ 'coop@mozilla.com',
+ 'jhford@mozilla.com',
+ 'joduinn@mozilla.com',
+ 'lsblakk@mozilla.com',
+ 'nthomas@mozilla.com',
+);
$dbh = new PDO("sqlite:$CLOBBERER_DB");
if (!$dbh) {
@@ -22,19 +65,49 @@
$q = $dbh->query('SELECT count(*) FROM sqlite_master WHERE NAME="clobber_times"');
$exists = $q->fetch(PDO::FETCH_NUM);
if (!$exists or !$exists[0]) {
+ $res = $dbh->exec('CREATE TABLE builds ('
+ .'id INTEGER PRIMARY KEY AUTOINCREMENT,'
+ .'master VARCHAR(100),'
+ .'branch VARCHAR(50),'
+ .'buildername VARCHAR(100),'
+ .'builddir VARCHAR(100),'
+ .'slave VARCHAR(30),'
+ .'last_build_time INTEGER)');
+ if ($res === FALSE) {
+ die(print_r($dbh->errorInfo(), TRUE));
+ }
+
$res = $dbh->exec('CREATE TABLE clobber_times ('
.'id INTEGER PRIMARY KEY AUTOINCREMENT,'
+ .'master VARCHAR(100),'
.'branch VARCHAR(50),'
- .'builder VARCHAR(50),'
+ .'builddir VARCHAR(100),'
.'slave VARCHAR(30),'
- .'lastclobber VARCHAR(30),'
- .'clobberer VARCHAR(50))');
+ .'lastclobber INTEGER,'
+ .'who VARCHAR(50))');
if ($res === FALSE) {
die(print_r($dbh->errorInfo(), TRUE));
}
chmod($CLOBBERER_DB, 0660);
}
+function isSpecial($user)
+{
+ // TODO: Figure out if we can use LDAP to get the group of $user
+ global $SPECIAL_PEOPLE;
+ return in_array($user, $SPECIAL_PEOPLE);
+}
+
+function canSee($builddir, $user)
+{
+ global $RELEASE_BUILDERS;
+ if (!in_array($builddir, $RELEASE_BUILDERS)) {
+ return true;
+ }
+
+ return isSpecial($user);
+}
+
function b64_encode($s)
{
return rtrim(base64_encode($s), "=");
@@ -46,20 +119,135 @@ function e($str)
return $dbh->quote($str);
}
+function getBuilders($slave)
+{
+ global $dbh;
+ $slave = e($slave);
+ $retval = array();
+ $builders = $dbh->query("SELECT DISTINCT builddir from builds where slave=$slave");
+ while ($r = $builders->fetch(PDO::FETCH_ASSOC)) {
+ // Find the most recent build for this builder
+ $builddir = e($r['builddir']);
+ $build = $dbh->query("SELECT buildername, builddir, branch FROM builds WHERE builddir = $builddir ORDER by last_build_time DESC LIMIT 1");
+ $r = $build->fetch(PDO::FETCH_ASSOC);
+ if ($r) {
+ $retval[] = $r;
+ }
+ }
+ return $retval;
+}
+
+function getMasters()
+{
+ global $dbh;
+ $retval = array();
+ $masters = $dbh->query("SELECT DISTINCT master from builds");
+ while ($r = $masters->fetch(PDO::FETCH_ASSOC)) {
+ $retval[] = $r['master'];
+ }
+ return $retval;
+}
+
+function updateBuildTime($master, $branch, $buildername, $builddir, $slave)
+{
+ global $dbh;
+ $master = e($master);
+ $branch = e($branch);
+ $buildername = e($buildername);
+ $builddir = e($builddir);
+ $slave = e($slave);
+ $now = time();
+
+ $rows = $dbh->exec("UPDATE builds SET last_build_time = $now WHERE master=$master AND "
+ ."branch=$branch AND buildername=$buildername AND slave=$slave");
+ if ($rows == 0) {
+ $dbh->exec("INSERT INTO builds "
+ ."(master, branch, buildername, builddir, slave, last_build_time) VALUES "
+ ."($master, $branch, $buildername, $builddir, $slave, $now)");
+ return true;
+ }
+ return false;
+}
+
+function getClobberTime($master, $branch, $builddir, $slave)
+{
+ global $dbh;
+ $master = e($master);
+ $branch = e($branch);
+ $builddir = e($builddir);
+ $slave = e($slave);
+ $q = "SELECT id, who, lastclobber FROM clobber_times WHERE "
+ ."builddir = $builddir AND (branch IS NULL OR branch = $branch) AND "
+ ."(master IS NULL OR master = $master) AND (slave IS NULL OR slave = $slave) "
+ ."ORDER BY lastclobber DESC LIMIT 1";
+ $s = $dbh->query($q);
+ $r = $s->fetch(PDO::FETCH_ASSOC);
+ if (!$r) {
+ return null;
+ }
+ else
+ {
+ return $r;
+ }
+}
+
//
// Handle form submission
//
if ($_POST['form_submitted']) {
$clobbers = array();
$slaves = array();
+ $user = $_SERVER['REMOTE_USER'];
+ $e_user = e($user);
+ $now = time();
foreach ($_POST as $k => $v) {
+ if ($k == "master") {
+ if (isSpecial($user)) {
+ $branch = $_POST['branch'];
+ if ($branch != '') {
+ $branch = e($branch);
+ } else {
+ $branch = 'NULL';
+ }
+ $builddir = $_POST['builddir'];
+ if ($builddir != '') {
+ $builders = array($builddir);
+ } else {
+ $builders = $RELEASE_BUILDERS;
+ }
+ if ($v != '') {
+ $master = e($v);
+ } else {
+ $master = 'NULL';
+ }
+ foreach ($builders as $builddir) {
+ $builddir = e($builddir);
+ $q = "INSERT INTO clobber_times "
+ ."(master, branch, builddir, slave, who, lastclobber) VALUES "
+ ."($master, $branch, $builddir, NULL, $e_user, $now)";
+ $dbh->exec($q) or die(print_r($dbh->errorInfo(), TRUE));
+ }
+ }
+ continue;
+ }
$t = explode('-', $k, 2);
// We only care about slave-<$row_id>
// This corresponds to a row that specifies which branch/builder/slave to clobber
if ($t[0] == 'slave') {
$row_id = e($t[1]);
- $user = e($_SERVER['REMOTE_USER']);
- $dbh->exec("UPDATE clobber_times SET lastclobber = DATETIME(\"NOW\"), clobberer = $user WHERE id = $row_id");
+ $s = $dbh->query("SELECT * from builds where id = $row_id");
+ $r = $s->fetch(PDO::FETCH_ASSOC);
+ if ($r)
+ {
+ $builddir = e($r['builddir']);
+ $branch = e($r['branch']);
+ $slave = e($r['slave']);
+ if (canSee($builddir, $user)) {
+ $dbh->exec("INSERT INTO clobber_times "
+ ."(master, branch, builddir, slave, who, lastclobber) VALUES "
+ ."(NULL, $branch, $builddir, $slave, $e_user, $now)") or die(print_r($$dbh->errorInfo(), TRUE));
+ }
+ }
}
}
// Redirect the user to the main page
@@ -68,11 +256,13 @@ function e($str)
header("Location: " . $_SERVER['REQUEST_URI']);
}
-$builder = $_GET['builder'];
-$slave = $_GET['slave'];
-$branch = $_GET['branch'];
+$buildername = urldecode($_GET['buildername']);
+$builddir = urldecode($_GET['builddir']);
+$slave = urldecode($_GET['slave']);
+$branch = urldecode($_GET['branch']);
+$master = urldecode($_GET['master']);
// Show the administration page if no clobber time is being queried
-if (!$builder) {
+if (!$buildername) {
?>
<html>
<head>
@@ -123,6 +313,61 @@ function toggleall(node, klass)
<a href="https://wiki.mozilla.org/Build:ClobberingATinderbox">Build:ClobberingATinderbox</a>
and/or <a href="https://wiki.mozilla.org/Clobbering_the_Tree">Clobbering the Tree</a>
for more information about what this page is for, and how to use it.</p>
+<?php
+ if (in_array($_SERVER['REMOTE_USER'], $SPECIAL_PEOPLE)) {
+?>
+<h1>Release Clobbers</h1>
+<form method="POST">
+<input type="hidden" name="form_submitted" value="true">
+Clobber all release builders on <select name="master">
+<option value="">Any master</option>
+<?php
+ $masters = getMasters();
+ foreach ($masters as $master) {
+ $e_master = htmlspecialchars($master);
+ print "<option value=\"$e_master\">$master</option>\n";
+ }
+?>
+</select>
+<select name="branch">
+<option value="">Any release</option>
+<?php
+ $builders = "";
+ $first = true;
+ foreach ($RELEASE_BUILDERS as $b) {
+ if (!$first) {
+ $builders .= ",";
+ }
+ $first = false;
+ $builders .= e($b);
+ }
+ $releases = $dbh->query("SELECT DISTINCT branch FROM builds WHERE builddir IN ($builders)");
+ while ($release = $releases->fetch(PDO::FETCH_ASSOC)) {
+ $release = $release['branch'];
+ $e_release = htmlspecialchars($release);
+ print "<option value=\"$e_release\">$release</option>\n";
+ }
+?>
+</select>
+<select name="builddir">
+<option value="">Any builder</option>
+<?php
+ $builders = "";
+ $first = true;
+ foreach ($RELEASE_BUILDERS as $b) {
+ $e_b = htmlspecialchars($b);
+ print "<option value=\"$e_b\">$b</option>\n";
+ }
+?>
+</select>
+
+<input type="submit" value="Wipe them out!">
+</form>
+
+<h1>Regular Clobbers</h1>
+
+<?php } ?>
+
<form method="POST">
<table border="1" cellspacing="0" cellpadding="1">
<thead>
@@ -130,23 +375,26 @@ function toggleall(node, klass)
</thead>
<tbody>
<?php
- $allbuilders = $dbh->query('SELECT * FROM clobber_times ORDER BY branch ASC, builder ASC');
+ $allbuilders = $dbh->query('SELECT DISTINCT id, branch, builddir, buildername, slave FROM builds ORDER BY branch ASC, buildername ASC');
if ($allbuilders) {
$last_branch = null;
$last_builder = null;
- // First pass: count the number of rows for each branch / builder so we can
+ // First pass: count the number of rows for each branch / buildername so we can
// set the 'rowspan' attribute
$rows_per_branch = array();
$rows_per_builder = array();
$rows = array();
while ($r = $allbuilders->fetch(PDO::FETCH_ASSOC)) {
+ if (!canSee($r['builddir'], $_SERVER['REMOTE_USER'])) {
+ continue;
+ }
$rows[] = $r;
- $builder = $r['builder'];
+ $buildername = $r['buildername'];
$branch = $r['branch'];
- if (!array_key_exists($builder, $rows_per_builder)) {
- $rows_per_builder[$builder] = 1;
+ if (!array_key_exists($buildername, $rows_per_builder)) {
+ $rows_per_builder[$buildername] = 1;
} else {
- $rows_per_builder[$builder] += 1;
+ $rows_per_builder[$buildername] += 1;
}
if (!array_key_exists($branch, $rows_per_branch)) {
$rows_per_branch[$branch] = 1;
@@ -160,7 +408,7 @@ function sort_func($r1, $r2) {
if ($c1 != 0) {
return $c1;
}
- $c2 = strnatcmp($r1['builder'], $r2['builder']);
+ $c2 = strnatcmp($r1['buildername'], $r2['buildername']);
if ($c2 != 0) {
return $c2;
}
@@ -177,25 +425,26 @@ function sort_func($r1, $r2) {
print "<input type=\"checkbox\" id=\"$branch_id\" onchange=\"toggleall(this, &quot;$branch_id&quot;)\" />";
print htmlspecialchars($r['branch']) . "</td>\n";
}
- if ($last_builder != $r['builder']) {
- $rowspan = $rows_per_builder[$r['builder']];
- $builder_id = b64_encode($r['builder']);
+ if ($last_builder != $r['buildername']) {
+ $rowspan = $rows_per_builder[$r['buildername']];
+ $builder_id = b64_encode($r['buildername']);
$classes = b64_encode($r['branch']);
print "<td rowspan=\"$rowspan\"><input type=\"checkbox\" id=\"$builder_id\" class=\"$classes\" onchange=\"toggleall(this, &quot;$builder_id&quot;)\" />";
- print htmlspecialchars($r['builder']) . "</td>\n";
+ print htmlspecialchars($r['buildername']) . "</td>\n";
}
- $classes = b64_encode($r['builder']) . " " . b64_encode($r['branch']);
+ $classes = b64_encode($r['buildername']) . " " . b64_encode($r['branch']);
$name = "slave-" . $r['id'];
print "<td><input type=\"checkbox\" name=\"$name\" class=\"$classes\" onchange=\"toggleall(this)\" />";
print htmlspecialchars($r['slave']) . "</td>\n";
- if ($r['lastclobber']) {
- print "<td>" . htmlspecialchars($r['lastclobber']) . "(UTC) by " . htmlspecialchars($r['clobberer']) . "</td>\n";
+ $lastclobber = getClobberTime(null, $r['branch'], $r['builddir'], $r['slave']);
+ if ($lastclobber) {
+ print "<td>" . strftime("%Y-%m-%d %H:%M:%S %Z", $lastclobber['lastclobber']) . " by " . htmlspecialchars($lastclobber['who']) . "</td>\n";
} else {
print "<td></td>\n";
}
print "</tr>\n";
$last_branch = $r['branch'];
- $last_builder = $r['builder'];
+ $last_builder = $r['buildername'];
}
} else {
print "<tr><td colspan=\"9\">No data</td></tr>\n";
@@ -213,22 +462,46 @@ function sort_func($r1, $r2) {
}
// Handle requests from slaves asking about their last clobber date
-$e_builder = e($builder);
-$e_slave = e($slave);
-$e_branch = e($branch);
-$s = $dbh->query("SELECT id, lastclobber FROM clobber_times WHERE builder = $e_builder AND slave = $e_slave AND branch = $e_branch");
-$r = $s->fetch(PDO::FETCH_ASSOC);
-if (!$r) {
- // If this branch/builder/slave combination doesn't yet exist in the
- // database, then insert it
- $res = $dbh->exec("INSERT INTO clobber_times (branch, builder, slave) VALUES ($e_branch, $e_builder, $e_slave)");
- if ($res === false) {
- header("HTTP/1.x 500 Internal Server Error");
- print "Couldn't insert row<br/>\n";
- die(print_r($dbh->errorInfo(), TRUE));
+
+// First, find the list of builders for this slave
+$slave_builders = getBuilders($slave);
+
+// Make sure that the current branch/builder is in that list
+$found = false;
+foreach ($slave_builders as $sb) {
+ if ($sb['builddir'] == $builddir && $sb['branch'] == $branch) {
+ $found = true;
+ break;
+ }
+}
+if (!$found) {
+ $slave_builders[] = array('builddir' => $builddir, 'branch' => $branch);
+}
+
+// And check the clobber time for each buildername
+$clobber_times = array();
+foreach ($slave_builders as $sb) {
+ $r = getClobberTime($master, $sb['branch'], $sb['builddir'], $slave);
+ if ($r) {
+ if (!array_key_exists($sb['builddir'], $clobber_times)) {
+ $clobber_times[$sb['builddir']] = array('lastclobber'=>$r['lastclobber'], 'who'=>$r['who']);
+ } else {
+ $t = $clobber_times[$sb['builddir']]['lastclobber'];
+ if ($r['lastclobber'] > $t) {
+ $clobber_times[$sb['builddir']] = array('lastclobber'=>$r['lastclobber'], 'who'=>$r['who']);
+ }
+ }
}
}
-else {
- print $r['lastclobber'];
+
+// Tell the slave what to clobber
+foreach ($clobber_times as $b => $r) {
+ $lastclobber = $r['lastclobber'];
+ $who = $r['who'];
+ print "$b:$lastclobber:$who\n";
}
+
+// Finally, update our table of when builds are happening
+$new = updateBuildTime($master, $branch, $buildername, $builddir, $slave);
+
?>
View
357 clobberer/test_clobberer.py
@@ -0,0 +1,357 @@
+from unittest import TestCase
+import sqlite3
+import os
+import subprocess
+import urllib
+import time
+import shutil
+
+###
+# For testing, update the values below to be suitable to your testing
+# environment
+###
+clobberURL = "http://localhost/~catlee/index.php"
+dbFile = "/home/catlee/public_html/db/clobberer.db"
+testDir = 'test-dir'
+
+###
+# Various utility functions for setting up a test case
+###
+def updateBuild(branch, buildername, builddir, slave, master):
+ """Send an update to the server to indicate that a slave is doing a build.
+ Returns the server's response."""
+ params = dict(branch=branch, buildername=buildername, builddir=builddir,
+ slave=slave, master=master)
+ url = "%s?%s" % (clobberURL, urllib.urlencode(params))
+ data = urllib.urlopen(url).read().strip()
+ return data
+
+def setClobber(branch, builddir, slave, master, now):
+ """Schedules a clobber by inserting data directly into the database."""
+ db = sqlite3.connect(dbFile)
+ db.execute("""INSERT INTO clobber_times
+ (master, branch, builddir, slave, lastclobber, who)
+ VALUES (?, ?, ?, ?, ?, 'testuser')""",
+ (master, branch, builddir, slave, now))
+ db.commit()
+
+def getClobbers():
+ """Returns a list of all entries in the clobber_times table"""
+ db = sqlite3.connect(dbFile)
+ res = db.execute("SELECT * FROM clobber_times")
+ return res.fetchall()
+
+def getBuilds():
+ """Returns a list of all entries in the builds table"""
+ db = sqlite3.connect(dbFile)
+ res = db.execute("SELECT * FROM builds")
+ return res.fetchall()
+
+def makeBuildDir(name, t):
+ """Create a fake build directory in our testDir, with last-clobber set to
+ `t`"""
+ builddir = os.path.join(testDir, name)
+ if not os.path.exists(builddir):
+ os.makedirs(builddir)
+ open(os.path.join(builddir, 'last-clobber'), "w").write(str(t))
+
+def runClobberer(branch, buildername, builddir, slave, master, periodic=None,
+ dry_run=True):
+ """Run the clobberer.py script, and return the output"""
+ if not os.path.exists(testDir):
+ os.makedirs(testDir)
+ cmd = ['python', os.path.abspath('clobberer.py'), '-v']
+ if periodic:
+ cmd.extend(['-t', str(periodic)])
+ if dry_run:
+ cmd.append('-n')
+ cmd.extend([clobberURL, branch, buildername, builddir, slave, master])
+ p = subprocess.Popen(cmd, cwd=testDir, stdout=subprocess.PIPE)
+ p.wait()
+ return p.stdout.read()
+
+###
+# Test cases
+###
+class TestClobber(TestCase):
+ def setUp(self):
+ if os.path.exists(dbFile):
+ os.unlink(dbFile)
+ # Hit the clobberURL to create the database file
+ urllib.urlopen(clobberURL).read()
+
+ # Create a working directory
+ if os.path.exists(testDir):
+ shutil.rmtree(testDir)
+ os.makedirs(testDir)
+
+ def tearDown(self):
+ if os.path.exists(testDir):
+ shutil.rmtree(testDir)
+
+ def testUpdateBuild(self):
+ # Test that build entries are getting into the DB properly
+ updateBuild("branch1", "My Builder", "mybuilder", "slave01", "master01");
+ updateBuild("branch1", "My Builder 2", "mybuilder2", "slave01", "master01");
+
+ builds = getBuilds()
+ # Strip out db id and time
+ builds = [b[1:-1] for b in builds]
+ builds.sort()
+ self.assertEquals(len(builds), 2)
+ self.assertEquals(builds[0],
+ ("master01", "branch1", "My Builder", "mybuilder", "slave01"))
+ self.assertEquals(builds[1],
+ ("master01", "branch1", "My Builder 2", "mybuilder2", "slave01")
+ )
+
+ def testUpdateBuildWithClobber(self):
+ # Test that build entries are getting into the DB properly
+ # this time when a clobber is set
+ now = int(time.time())
+ setClobber("branch1", "mybuilder", "slave01", None, now)
+ updateBuild("branch1", "My Builder", "mybuilder", "slave01", "master01");
+ updateBuild("branch1", "My Builder 2", "mybuilder2", "slave01", "master01");
+
+ builds = getBuilds()
+ # Strip out db id and time
+ builds = [b[1:-1] for b in builds]
+ builds.sort()
+ self.assertEquals(len(builds), 2)
+ self.assertEquals(builds[0],
+ ("master01", "branch1", "My Builder", "mybuilder", "slave01"))
+ self.assertEquals(builds[1],
+ ("master01", "branch1", "My Builder 2", "mybuilder2", "slave01")
+ )
+
+ def testSetSlaveClobber(self):
+ # Clobber one builder on one slave, regardless of master
+ now = int(time.time())
+ setClobber("branch1", "mybuilder", "slave01", None, now)
+
+ # Add a build on another builder, to make sure it's not getting
+ # clobbered
+ data = updateBuild("branch1", "My Builder 2", "mybuilder2", "slave01",
+ "master01")
+ self.assert_("mybuilder2" not in data, data)
+
+ # Check that build on master01 gets clobbered
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave01",
+ "master01")
+ self.assertEquals(data, "mybuilder:%s:testuser" % now)
+
+ # Check that build on master02 gets clobbered
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave01",
+ "master02")
+ self.assertEquals(data, "mybuilder:%s:testuser" % now)
+
+ def testSetMasterClobber(self):
+ # Clobber one builder on any slave on one master
+ now = int(time.time())
+ setClobber("branch1", "mybuilder", None, "master01", now)
+
+ # Add a build on another builder, to make sure it's not getting
+ # clobbered
+ data = updateBuild("branch1", "My Builder 2", "mybuilder2", "slave01",
+ "master01")
+ self.assert_("mybuilder2" not in data, data)
+
+ # Check that the build is clobbered on all slaves on master01
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave01",
+ "master01")
+ self.assertEquals(data, "mybuilder:%s:testuser" % now)
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave02",
+ "master01")
+ self.assertEquals(data, "mybuilder:%s:testuser" % now)
+
+ # But not on master02
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave01",
+ "master02")
+ self.assertEquals(data, "")
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave02",
+ "master02")
+ self.assertEquals(data, "")
+
+ def testSetSlaveMasterClobber(self):
+ # Clobber one builder on one slave on one master
+ now = int(time.time())
+ setClobber("branch1", "mybuilder", "slave01", "master01", now)
+
+ # Add a build on another builder, to make sure it's not getting
+ # clobbered
+ data = updateBuild("branch1", "My Builder 2", "mybuilder2", "slave01",
+ "master01")
+ self.assert_("mybuilder2" not in data, data)
+
+ # Check that the build is clobbered on master01
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave01",
+ "master01")
+ self.assertEquals(data, "mybuilder:%s:testuser" % now)
+
+ # But not on the other slave
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave02",
+ "master01")
+ self.assertEquals(data, "")
+
+ # Or on the other master
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave01",
+ "master02")
+ self.assertEquals(data, "")
+ data = updateBuild("branch1", "My Builder", "mybuilder", "slave02",
+ "master02")
+ self.assertEquals(data, "")
+
+ def testSlaveClobber(self):
+ # Test that the client will do a clobber if we tell it to
+ now = int(time.time())
+
+ makeBuildDir('mybuilder', now)
+ setClobber('branch1', 'mybuilder', 'slave01', 'master01', now+1)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01')
+ self.assert_('mybuilder:Server is forcing a clobber' in data, data)
+
+ def testSlaveNoClobber(self):
+ # Test that the client won't clobber if the server's clobber date is
+ # too old
+ now = int(time.time())
+
+ makeBuildDir('mybuilder', now+1)
+ setClobber('branch1', 'mybuilder', 'slave01', 'master01', now)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01')
+ self.assert_('mybuilder:Server is forcing a clobber' not in data, data)
+
+ def testSlaveClobberOther(self):
+ # Test that other builders than the one we're running will get
+ # clobbered
+ now = int(time.time())
+
+ makeBuildDir('mybuilder', now)
+ makeBuildDir('linux_build', now)
+ updateBuild('branch1', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+
+ setClobber('branch1', 'mybuilder', 'slave01', 'master01', now-1)
+ setClobber('branch1', 'linux_build', None, 'master01', now+1)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01')
+ self.assert_('mybuilder:Server is forcing a clobber' not in data, data)
+ self.assert_('linux_build:Server is forcing a clobber' in data, data)
+
+ def testSlavePeriodicClobber(self):
+ # Test that periodic clobbers happen if it's been longer than the
+ # specified time since our last clobber
+ now = int(time.time())
+
+ makeBuildDir('mybuilder', now-3601)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01', 1)
+ self.assert_('mybuilder:More than' in data, data)
+
+ def testSlaveNoPeriodicClobber(self):
+ # Test that periodic clobbers don't happen if it hasn't been longer
+ # than the specified time since our last clobber
+ now = int(time.time())
+
+ makeBuildDir('mybuilder', now-3599)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01', 1)
+ self.assert_('mybuilder:More than' not in data, data)
+
+ def testSlaveNoPeriodicClobberOther(self):
+ # Test that periodic clobbers don't happen on builders other than the
+ # 'current' builder
+ now = int(time.time())
+
+ makeBuildDir('mybuilder', now-3599)
+ makeBuildDir('mybuilder2', now-3601)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01', 1)
+ self.assert_('mybuilder:More than' not in data, data)
+ self.assert_('mybuilder2:More than' not in data, data)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder2', 'slave01',
+ 'master01', 1)
+ self.assert_('mybuilder2:More than' in data, data)
+
+ def testSlaveClobberRelease(self):
+ # Test that clobbers on release builders work
+ now = int(time.time())
+ makeBuildDir('linux_build', now-10)
+ updateBuild('branch1', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ updateBuild('branch2', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ now = int(time.time())
+ setClobber('branch2', 'linux_build', None, None, now)
+
+ data = runClobberer('branch2', 'Linux Release Build', 'linux_build',
+ 'slave01', 'master01')
+ self.assert_('linux_build:Server is forcing' in data, data)
+
+ def testSlaveClobberReleaseOtherBranch(self):
+ # Test that clobbers on release builders work
+ now = int(time.time())
+ makeBuildDir('linux_build', now-10)
+ updateBuild('branch1', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ updateBuild('branch2', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ setClobber('branch2', 'linux_build', None, None, now)
+
+ # Even though we're running a different branch for _this_ run, our last
+ # run was on branch2, so it should be clobbered
+ data = runClobberer('branch1', 'Linux Release Build', 'linux_build',
+ 'slave01', 'master01')
+ self.assert_('linux_build:Server is forcing' in data, data)
+
+ def testSlaveClobberReleaseNotOtherBranch(self):
+ # Test that clobbers on release builders don't clobber builds from
+ # other branches
+ now = int(time.time())
+ makeBuildDir('linux_build', now-10)
+ updateBuild('branch2', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ updateBuild('branch1', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ now = int(time.time())
+ setClobber('branch2', 'linux_build', None, None, now)
+
+ data = runClobberer('branch1', 'Linux Release Build', 'linux_build',
+ 'slave01', 'master01')
+ self.assert_('linux_build:Server is forcing' not in data, data)
+
+ def testSlaveClobberReleaseOtherBranchOtherBuilder(self):
+ # Test that clobbers on release builders don't clobber builds from
+ # other branches when run from another builder
+ now = int(time.time())
+ makeBuildDir('linux_build', now-10)
+ updateBuild('branch1', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ updateBuild('branch2', 'linux_build', 'linux_build', 'slave01',
+ 'master01')
+ time.sleep(1)
+ now = int(time.time())
+ setClobber('branch1', 'linux_build', None, None, now)
+
+ data = runClobberer('branch1', 'My Builder', 'mybuilder', 'slave01',
+ 'master01')
+ self.assert_('linux_build:Server is forcing' not in data, data)
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main()

0 comments on commit 82b7cea

Please sign in to comment.
Something went wrong with that request. Please try again.