Add more error checking to preflight queue.

What's new?
  - cros_mark_as_stable now exits with errors if directories are specified
    that don't exist.
  - cbuildbot.py always explicitly specifies overlay directories so
    cros_mark_as_stable can rely on them existing.
  - Package names and paths are now separated with colons instead of
    spaces, so as to allow for us using the same syntax with
    enter_chroot.sh as we use without the same script. (enter_chroot.sh
    mucks with command-lines that contain spaces or quotes.)
  - cbuildbot.py now ensures its build path is a absolute path. This ensures we don't kill the wrong processes, if, for instance, the buildpath is '../..'
  - All buildbots now explicitly specify what overlays they want to rev. Public buildbots only rev public ebuilds and private buildbots now only rev private ebuilds.

BUG=chromium-os:8647
TEST=Ran unit tests. Manually marked packages as stable. Ran cbuildbot.py test run.

Change-Id: I1df6d428973d91329c4f5159e2886889a3ebb7c7

Review URL: http://codereview.chromium.org/4442001
This commit is contained in:
David James 2010-11-12 14:33:14 -08:00
parent bcc60b625d
commit 795bd30f06
4 changed files with 104 additions and 76 deletions

View File

@ -18,7 +18,8 @@ import cbuildbot_comm
from cbuildbot_config import config from cbuildbot_config import config
sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) sys.path.append(os.path.join(os.path.dirname(__file__), '../lib'))
from cros_build_lib import Die, Info, RunCommand, Warning from cros_build_lib import (Die, Info, ReinterpretPathForChroot, RunCommand,
Warning)
_DEFAULT_RETRIES = 3 _DEFAULT_RETRIES = 3
ARCHIVE_BASE = '/var/www/archive' ARCHIVE_BASE = '/var/www/archive'
@ -181,37 +182,38 @@ def _ParseRevisionString(revision_string, repo_dictionary):
return revisions.items() return revisions.items()
def _UprevFromRevisionList(buildroot, tracking_branch, revision_list, board): def _UprevFromRevisionList(buildroot, tracking_branch, revision_list, board,
overlays):
"""Uprevs based on revision list.""" """Uprevs based on revision list."""
if not revision_list: if not revision_list:
Info('No packages found to uprev') Info('No packages found to uprev')
return return
package_str = '' packages = []
for package, revision in revision_list: for package, revision in revision_list:
package_str += package + ' ' assert ':' not in package, 'Invalid package name: %s' % package
packages.append(package)
package_str = package_str.strip() chroot_overlays = [ReinterpretPathForChroot(path) for path in overlays]
cwd = os.path.join(buildroot, 'src', 'scripts') cwd = os.path.join(buildroot, 'src', 'scripts')
# TODO(davidjames): --foo="bar baz" only works here because we're using
# enter_chroot.
RunCommand(['./cros_mark_as_stable', RunCommand(['./cros_mark_as_stable',
'--board=%s' % board, '--board=%s' % board,
'--tracking_branch="%s"' % tracking_branch, '--tracking_branch=%s' % tracking_branch,
'--packages="%s"' % package_str, '--overlays=%s' % ':'.join(chroot_overlays),
'--packages=%s' % ':'.join(packages),
'commit'], 'commit'],
cwd=cwd, enter_chroot=True) cwd=cwd, enter_chroot=True)
def _UprevAllPackages(buildroot, tracking_branch, board): def _UprevAllPackages(buildroot, tracking_branch, board, overlays):
"""Uprevs all packages that have been updated since last uprev.""" """Uprevs all packages that have been updated since last uprev."""
cwd = os.path.join(buildroot, 'src', 'scripts') cwd = os.path.join(buildroot, 'src', 'scripts')
# TODO(davidjames): --foo="bar baz" only works here because we're using chroot_overlays = [ReinterpretPathForChroot(path) for path in overlays]
# enter_chroot.
RunCommand(['./cros_mark_as_stable', '--all', RunCommand(['./cros_mark_as_stable', '--all',
'--board=%s' % board, '--board=%s' % board,
'--tracking_branch="%s"' % tracking_branch, 'commit'], '--overlays=%s' % ':'.join(chroot_overlays),
'--tracking_branch=%s' % tracking_branch, 'commit'],
cwd=cwd, enter_chroot=True) cwd=cwd, enter_chroot=True)
@ -228,12 +230,13 @@ def _GetVMConstants(buildroot):
return (vdisk_size.strip(), statefulfs_size.strip()) return (vdisk_size.strip(), statefulfs_size.strip())
def _GitCleanup(buildroot, board, tracking_branch): def _GitCleanup(buildroot, board, tracking_branch, overlays):
"""Clean up git branch after previous uprev attempt.""" """Clean up git branch after previous uprev attempt."""
cwd = os.path.join(buildroot, 'src', 'scripts') cwd = os.path.join(buildroot, 'src', 'scripts')
if os.path.exists(cwd): if os.path.exists(cwd):
RunCommand(['./cros_mark_as_stable', '--srcroot=..', RunCommand(['./cros_mark_as_stable', '--srcroot=..',
'--board=%s' % board, '--board=%s' % board,
'--overlays=%s' % ':'.join(overlays),
'--tracking_branch=%s' % tracking_branch, 'clean'], '--tracking_branch=%s' % tracking_branch, 'clean'],
cwd=cwd, error_ok=True) cwd=cwd, error_ok=True)
@ -257,9 +260,9 @@ def _WipeOldOutput(buildroot):
# =========================== Main Commands =================================== # =========================== Main Commands ===================================
def _PreFlightRinse(buildroot, board, tracking_branch): def _PreFlightRinse(buildroot, board, tracking_branch, overlays):
"""Cleans up any leftover state from previous runs.""" """Cleans up any leftover state from previous runs."""
_GitCleanup(buildroot, board, tracking_branch) _GitCleanup(buildroot, board, tracking_branch, overlays)
_CleanUpMountPoints(buildroot) _CleanUpMountPoints(buildroot)
RunCommand(['sudo', 'killall', 'kvm'], error_ok=True) RunCommand(['sudo', 'killall', 'kvm'], error_ok=True)
@ -347,7 +350,7 @@ def _RunSmokeSuite(buildroot, results_dir):
], cwd=cwd, error_ok=False) ], cwd=cwd, error_ok=False)
def _UprevPackages(buildroot, tracking_branch, revisionfile, board): def _UprevPackages(buildroot, tracking_branch, revisionfile, board, overlays):
"""Uprevs a package based on given revisionfile. """Uprevs a package based on given revisionfile.
If revisionfile is set to None or does not resolve to an actual file, this If revisionfile is set to None or does not resolve to an actual file, this
@ -376,26 +379,19 @@ def _UprevPackages(buildroot, tracking_branch, revisionfile, board):
# print >> sys.stderr, 'CBUILDBOT Revision list found %s' % revisions # print >> sys.stderr, 'CBUILDBOT Revision list found %s' % revisions
# revision_list = _ParseRevisionString(revisions, # revision_list = _ParseRevisionString(revisions,
# _CreateRepoDictionary(buildroot, board)) # _CreateRepoDictionary(buildroot, board))
# _UprevFromRevisionList(buildroot, tracking_branch, revision_list, board) # _UprevFromRevisionList(buildroot, tracking_branch, revision_list, board,
# overlays)
#else: #else:
Info('CBUILDBOT Revving all') Info('CBUILDBOT Revving all')
_UprevAllPackages(buildroot, tracking_branch, board) _UprevAllPackages(buildroot, tracking_branch, board, overlays)
def _UprevPush(buildroot, tracking_branch, board, overlays): def _UprevPush(buildroot, tracking_branch, board, overlays):
"""Pushes uprev changes to the main line.""" """Pushes uprev changes to the main line."""
cwd = os.path.join(buildroot, 'src', 'scripts') cwd = os.path.join(buildroot, 'src', 'scripts')
public_overlay = '%s/src/third_party/chromiumos-overlay' % buildroot
private_overlay = '%s/src/private-overlays/chromeos-overlay' % buildroot
if overlays == 'private':
overlays = [private_overlay]
elif overlays == 'public':
overlays = [public_overlay]
else:
overlays = [public_overlay, private_overlay]
RunCommand(['./cros_mark_as_stable', '--srcroot=..', RunCommand(['./cros_mark_as_stable', '--srcroot=..',
'--board=%s' % board, '--board=%s' % board,
'--overlays=%s' % " ".join(overlays), '--overlays=%s' % ':'.join(overlays),
'--tracking_branch=%s' % tracking_branch, '--tracking_branch=%s' % tracking_branch,
'--push_options=--bypass-hooks -f', 'push'], '--push_options=--bypass-hooks -f', 'push'],
cwd=cwd) cwd=cwd)
@ -467,6 +463,34 @@ def _GetConfig(config_name):
return buildconfig return buildconfig
def _ResolveOverlays(buildroot, overlays):
"""Return the list of overlays to use for a given buildbot.
Args:
buildroot: The root directory where the build occurs. Must be an absolute
path.
overlays: A string describing which overlays you want.
'private': Just the private overlay.
'public': Just the public overlay.
'both': Both the public and private overlays.
"""
public_overlay = '%s/src/third_party/chromiumos-overlay' % buildroot
private_overlay = '%s/src/private-overlays/chromeos-overlay' % buildroot
if overlays == 'private':
paths = [private_overlay]
elif overlays == 'public':
paths = [public_overlay]
elif overlays == 'both':
paths = [public_overlay, private_overlay]
else:
Die('Incorrect overlay configuration: %s' % overlays)
for path in paths:
assert ':' not in path, 'Overlay must not contain colons: %s' % path
if not os.path.isdir(path):
Die('Missing overlay: %s' % path)
return paths
def main(): def main():
# Parse options # Parse options
usage = "usage: %prog [options] cbuildbot_config" usage = "usage: %prog [options] cbuildbot_config"
@ -491,7 +515,7 @@ def main():
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
buildroot = options.buildroot buildroot = os.path.abspath(options.buildroot)
revisionfile = options.revisionfile revisionfile = options.revisionfile
tracking_branch = options.tracking_branch tracking_branch = options.tracking_branch
@ -502,8 +526,11 @@ def main():
parser.print_usage() parser.print_usage()
sys.exit(1) sys.exit(1)
# Calculate list of overlay directories.
overlays = _ResolveOverlays(buildroot, buildconfig['overlays'])
try: try:
_PreFlightRinse(buildroot, buildconfig['board'], tracking_branch) _PreFlightRinse(buildroot, buildconfig['board'], tracking_branch, overlays)
if options.clobber or not os.path.isdir(buildroot): if options.clobber or not os.path.isdir(buildroot):
_FullCheckout(buildroot, tracking_branch, url=options.url) _FullCheckout(buildroot, tracking_branch, url=options.url)
else: else:
@ -519,7 +546,7 @@ def main():
if buildconfig['uprev']: if buildconfig['uprev']:
_UprevPackages(buildroot, tracking_branch, revisionfile, _UprevPackages(buildroot, tracking_branch, revisionfile,
board=buildconfig['board']) buildconfig['board'], overlays)
_EnableLocalAccount(buildroot) _EnableLocalAccount(buildroot)
_Build(buildroot) _Build(buildroot)
@ -545,7 +572,7 @@ def main():
# Master bot needs to check if the other slaves completed. # Master bot needs to check if the other slaves completed.
if cbuildbot_comm.HaveSlavesCompleted(config): if cbuildbot_comm.HaveSlavesCompleted(config):
_UprevPush(buildroot, tracking_branch, buildconfig['board'], _UprevPush(buildroot, tracking_branch, buildconfig['board'],
buildconfig['overlays']) overlays)
else: else:
Die('CBUILDBOT - One of the slaves has failed!!!') Die('CBUILDBOT - One of the slaves has failed!!!')

View File

@ -19,9 +19,9 @@ hostname -- Needed for 'important' slaves. The hostname of the bot. Should
match hostname in slaves.cfg in buildbot checkout. match hostname in slaves.cfg in buildbot checkout.
unittests -- Runs unittests for packages. unittests -- Runs unittests for packages.
smoke_bvt -- Runs the test smoke suite in a qemu-based VM using KVM. smoke_bvt -- Runs the test smoke suite in a qemu-based VM using KVM.
overlays -- If this bot is a master bot, select what overlays to push changes overlays -- Select what overlays to look at. This can be 'public', 'private'
to. This can be 'public', 'private', or 'both'. There should only or 'both'. There should only be one master bot pushing changes to
be one bot pushing changes to each overlay. each overlay per branch.
""" """
@ -33,6 +33,7 @@ config['default'] = {
'important' : False, 'important' : False,
'unittests' : False, 'unittests' : False,
'smoke_bvt' : False, 'smoke_bvt' : False,
'overlays': 'public',
} }
config['x86-generic-pre-flight-queue'] = { config['x86-generic-pre-flight-queue'] = {
'board' : 'x86-generic', 'board' : 'x86-generic',
@ -69,6 +70,7 @@ config['x86_agz_bin'] = {
'important' : False, 'important' : False,
'unittests' : True, 'unittests' : True,
'smoke_bvt' : True, 'smoke_bvt' : True,
'overlays': 'private',
} }
config['x86_dogfood_bin'] = { config['x86_dogfood_bin'] = {
'board' : 'x86-dogfood', 'board' : 'x86-dogfood',
@ -77,52 +79,29 @@ config['x86_dogfood_bin'] = {
'important' : False, 'important' : False,
'unittests' : True, 'unittests' : True,
'smoke_bvt' : True, 'smoke_bvt' : True,
'overlays': 'private',
} }
config['x86_pineview_bin'] = { config['x86_pineview_bin'] = {
'board' : 'x86-pineview', 'board' : 'x86-pineview',
'uprev' : True, 'uprev' : True,
'master' : False, 'master' : False,
'important' : False, 'important' : False,
'hostname' : 'codf200.jail',
'unittests': True, 'unittests': True,
'overlays': 'public',
} }
config['arm_tegra2_bin'] = { config['arm_tegra2_bin'] = {
'board' : 'tegra2', 'board' : 'tegra2',
'uprev' : True, 'uprev' : True,
'master' : False, 'master' : False,
'important' : False, 'important' : False,
'hostname' : 'codg172.jail',
'unittests' : False,
}
config['arm_voguev210_bin'] = {
'board' : 'voguev210',
'uprev' : True,
'master' : False,
'important' : False,
'hostname' : 'codf196.jail',
'unittests' : False,
}
config['arm_beagleboard_bin'] = {
'board' : 'beagleboard',
'master' : False,
'uprev' : True,
'important' : False,
'hostname' : 'codf202.jail',
'unittests' : False,
}
config['arm_st1q_bin'] = {
'board' : 'st1q',
'uprev' : True,
'master' : False,
'important' : False,
'hostname' : 'codg158.jail',
'unittests' : False, 'unittests' : False,
'overlays': 'public',
} }
config['arm_generic_bin'] = { config['arm_generic_bin'] = {
'board' : 'arm-generic', 'board' : 'arm-generic',
'uprev' : True, 'uprev' : True,
'master' : False, 'master' : False,
'important' : False, 'important' : False,
'hostname' : 'codg175.jail',
'unittests' : False, 'unittests' : False,
'overlays': 'public',
} }

View File

@ -16,6 +16,7 @@ import unittest
# Fixes circular dependency error. # Fixes circular dependency error.
import cbuildbot_comm import cbuildbot_comm
import cbuildbot import cbuildbot
from cros_build_lib import ReinterpretPathForChroot
class CBuildBotTest(mox.MoxTestBase): class CBuildBotTest(mox.MoxTestBase):
@ -42,6 +43,10 @@ class CBuildBotTest(mox.MoxTestBase):
['dev-util/perf', '12345test'], ['dev-util/perf', '12345test'],
['chromos-base/libcros', '12345test'] ['chromos-base/libcros', '12345test']
] ]
self._overlays = ['%s/src/third_party/chromiumos-overlay' % self._buildroot]
self._chroot_overlays = [
ReinterpretPathForChroot(p) for p in self._overlays
]
def testParseRevisionString(self): def testParseRevisionString(self):
"""Test whether _ParseRevisionString parses string correctly.""" """Test whether _ParseRevisionString parses string correctly."""
@ -168,13 +173,15 @@ class CBuildBotTest(mox.MoxTestBase):
cbuildbot.RunCommand(['./cros_mark_as_stable', '--all', cbuildbot.RunCommand(['./cros_mark_as_stable', '--all',
'--board=%s' % self._test_board, '--board=%s' % self._test_board,
'--tracking_branch="cros/master"', 'commit'], '--overlays=%s' % ':'.join(self._chroot_overlays),
'--tracking_branch=cros/master', 'commit'],
cwd='%s/src/scripts' % self._buildroot, cwd='%s/src/scripts' % self._buildroot,
enter_chroot=True) enter_chroot=True)
self.mox.ReplayAll() self.mox.ReplayAll()
cbuildbot._UprevPackages(self._buildroot, self.tracking_branch, cbuildbot._UprevPackages(self._buildroot, self.tracking_branch,
self._revision_file, self._test_board) self._revision_file, self._test_board,
self._overlays)
self.mox.VerifyAll() self.mox.VerifyAll()
def testUprevAllPackages(self): def testUprevAllPackages(self):
@ -189,13 +196,15 @@ class CBuildBotTest(mox.MoxTestBase):
cbuildbot.RunCommand(['./cros_mark_as_stable', '--all', cbuildbot.RunCommand(['./cros_mark_as_stable', '--all',
'--board=%s' % self._test_board, '--board=%s' % self._test_board,
'--tracking_branch="cros/master"', 'commit'], '--overlays=%s' % ':'.join(self._chroot_overlays),
'--tracking_branch=cros/master', 'commit'],
cwd='%s/src/scripts' % self._buildroot, cwd='%s/src/scripts' % self._buildroot,
enter_chroot=True) enter_chroot=True)
self.mox.ReplayAll() self.mox.ReplayAll()
cbuildbot._UprevPackages(self._buildroot, self.tracking_branch, cbuildbot._UprevPackages(self._buildroot, self.tracking_branch,
self._revision_file, self._test_board) self._revision_file, self._test_board,
self._overlays)
self.mox.VerifyAll() self.mox.VerifyAll()

View File

@ -22,10 +22,10 @@ from cros_build_lib import Info, RunCommand, Warning, Die
gflags.DEFINE_string('board', '', gflags.DEFINE_string('board', '',
'Board for which the package belongs.', short_name='b') 'Board for which the package belongs.', short_name='b')
gflags.DEFINE_string('overlays', '', gflags.DEFINE_string('overlays', '',
'Space separated list of overlays to modify.', 'Colon-separated list of overlays to modify.',
short_name='o') short_name='o')
gflags.DEFINE_string('packages', '', gflags.DEFINE_string('packages', '',
'Space separated list of packages to mark as stable.', 'Colon-separated list of packages to mark as stable.',
short_name='p') short_name='p')
gflags.DEFINE_string('push_options', '', gflags.DEFINE_string('push_options', '',
'Options to use with git-cl push using push command.') 'Options to use with git-cl push using push command.')
@ -245,7 +245,11 @@ def _SimpleRunCommand(command):
"""Runs a shell command and returns stdout back to caller.""" """Runs a shell command and returns stdout back to caller."""
_Print(' + %s' % command) _Print(' + %s' % command)
proc_handle = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) proc_handle = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
return proc_handle.communicate()[0] stdout = proc_handle.communicate()[0]
retcode = proc_handle.wait()
if retcode != 0:
raise subprocess.CalledProcessError(retcode, command, output=stdout)
return stdout
# ======================= End Global Helper Functions ======================== # ======================= End Global Helper Functions ========================
@ -323,7 +327,7 @@ class _EBuild(object):
# Grab and evaluate CROS_WORKON variables from this ebuild. # Grab and evaluate CROS_WORKON variables from this ebuild.
unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version
cmd = ('CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s" ' cmd = ('export CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s"; '
'eval $(grep -E "^CROS_WORKON" %s) && ' 'eval $(grep -E "^CROS_WORKON" %s) && '
'echo $CROS_WORKON_PROJECT ' 'echo $CROS_WORKON_PROJECT '
'$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR' '$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR'
@ -341,10 +345,10 @@ class _EBuild(object):
# TODO(anush): This hack is only necessary because the kernel ebuild has # TODO(anush): This hack is only necessary because the kernel ebuild has
# 'if' statements, so we can't grab the CROS_WORKON_LOCALNAME properly. # 'if' statements, so we can't grab the CROS_WORKON_LOCALNAME properly.
# We should clean up the kernel ebuild and remove this hack. # We should clean up the kernel ebuild and remove this hack.
if not os.path.exists(srcdir) and subdir == 'kernel/': if not os.path.isdir(srcdir) and subdir == 'kernel/':
srcdir = os.path.join(srcroot, 'third_party/kernel/files') srcdir = os.path.join(srcroot, 'third_party/kernel/files')
if not os.path.exists(srcdir): if not os.path.isdir(srcdir):
Die('Cannot find commit id for %s' % self.ebuild_path) Die('Cannot find commit id for %s' % self.ebuild_path)
# Verify that we're grabbing the commit id from the right project name. # Verify that we're grabbing the commit id from the right project name.
@ -487,11 +491,16 @@ def main(argv):
except gflags.FlagsError, e : except gflags.FlagsError, e :
_PrintUsageAndDie(str(e)) _PrintUsageAndDie(str(e))
package_list = gflags.FLAGS.packages.split() package_list = gflags.FLAGS.packages.split(':')
_CheckSaneArguments(package_list, command) _CheckSaneArguments(package_list, command)
if gflags.FLAGS.overlays: if gflags.FLAGS.overlays:
overlays = dict((path, []) for path in gflags.FLAGS.overlays.split()) overlays = {}
for path in gflags.FLAGS.overlays.split(':'):
if not os.path.isdir(path):
Die('Cannot find overlay: %s' % path)
overlays[path] = []
else: else:
Warning('Missing --overlays argument')
overlays = { overlays = {
'%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [], '%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [],
'%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: [] '%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: []
@ -501,9 +510,13 @@ def main(argv):
_BuildEBuildDictionary(overlays, gflags.FLAGS.all, package_list) _BuildEBuildDictionary(overlays, gflags.FLAGS.all, package_list)
for overlay, ebuilds in overlays.items(): for overlay, ebuilds in overlays.items():
if not os.path.exists(overlay): if not os.path.isdir(overlay):
Warning("Skipping %s" % overlay) Warning("Skipping %s" % overlay)
continue continue
# TODO(davidjames): Currently, all code that interacts with git depends on
# the cwd being set to the overlay directory. We should instead pass in
# this parameter so that we don't need to modify the cwd globally.
os.chdir(overlay) os.chdir(overlay)
if command == 'clean': if command == 'clean':