# HG changeset patch # User cmlenz # Date 1126723932 0 # Node ID 9cabfdbdb8e0bac56e11767542cd7f7b2f11541e # Parent 6bb8e7a18072402cf7dc634b8f3bd77a8c81bbd0 * Improve error handling in the snapshot archive packing/unpacking code. Specificially, when a build slave fails to unpack a snapshot it received from the master, it'll report the error back to the master (and thereby reject the build request), instead of just bailing and leaving the master waiting for a reply. * Host names of slaves are now lower-cased (some windows machines would report the host all upper-case). * Minor fixes to some of the recipe commands. diff --git a/bitten/build/ctools.py b/bitten/build/ctools.py --- a/bitten/build/ctools.py +++ b/bitten/build/ctools.py @@ -14,13 +14,11 @@ log = logging.getLogger('bitten.build.ctools') -def make(ctxt, target=None, file_=None, jobs=None, keep_going=False): +def make(ctxt, target=None, file_=None, keep_going=False): """Execute a Makefile target.""" args = ['--directory', ctxt.basedir] if file_: args += ['--file', ctxt.resolve(file_)] - if jobs: - args += ['--jobs', int(jobs)] if keep_going: args.append('--keep-going') if target: diff --git a/bitten/build/shtools.py b/bitten/build/shtools.py --- a/bitten/build/shtools.py +++ b/bitten/build/shtools.py @@ -61,7 +61,8 @@ def pipe(ctxt, executable=None, file_=None, input_=None, output=None, args=None): """Pipe the contents of a file through a script.""" - assert file_, 'Missing required attribute "file"' + assert executable or file_, \ + 'Either "executable" or "file" attribute required' assert input_, 'Missing required attribute "file"' if args: diff --git a/bitten/slave.py b/bitten/slave.py --- a/bitten/slave.py +++ b/bitten/slave.py @@ -66,7 +66,7 @@ if self.session.name is not None: node = self.session.name else: - node = node.split('.', 1)[0] + node = node.split('.', 1)[0].lower() packages = [] if self.session.config is not None: @@ -137,9 +137,18 @@ log.debug('Received snapshot archive: %s', archive_path) # Unpack the archive - prefix = archive.unpack(archive_path, workdir) - path = os.path.join(workdir, prefix) - log.debug('Unpacked snapshot to %s' % path) + try: + prefix = archive.unpack(archive_path, workdir) + path = os.path.join(workdir, prefix) + log.debug('Unpacked snapshot to %s' % path) + except archive.Error, e: + xml = xmlio.Element('error', code=550)[ + 'Could not unpack archive (%s)' % e + ] + self.channel.send_err(msgno, beep.Payload(xml)) + log.error('Could not unpack archive %s: %s', archive_path, e, + exc_info=True) + return # Fix permissions for root, dirs, files in os.walk(workdir, topdown=False): @@ -151,7 +160,6 @@ self.execute_build(msgno, Recipe(self.recipe_xml, path)) def execute_build(self, msgno, recipe): - global log log.info('Building in directory %s', recipe.ctxt.basedir) try: if not self.session.dry_run: diff --git a/bitten/util/archive.py b/bitten/util/archive.py --- a/bitten/util/archive.py +++ b/bitten/util/archive.py @@ -15,6 +15,9 @@ _formats = {'gzip': ('.tar.gz', 'gz'), 'bzip2': ('.tar.bz2', 'bz2'), 'zip': ('.zip', None)} +class Error(Exception): + """Error raised when packing or unpacking a snapshot archive fails.""" + def index(env, prefix): """Generator that yields `(rev, format, path)` tuples for every archive in the environment snapshots directory that match the specified prefix. @@ -45,13 +48,14 @@ repos = env.get_repository() root = repos.get_node(path or '/', rev) if not root.isdir: - raise Exception, '"%s" is not a directory' % path + raise Error, '"%s" is not a directory' % path - assert format in _formats, 'Unknown archive format: %s' % format + if format not in _formats: + raise Error, 'Unknown archive format: %s' % format filedir = os.path.join(env.path, 'snapshots') if not os.access(filedir, os.R_OK + os.W_OK): - raise IOError, 'Insufficient permissions to create tarball' + raise Error, 'Insufficient permissions to create tarball' if not prefix: prefix = root.path.replace('/', '-') prefix += '_r%s' % root.rev @@ -73,17 +77,23 @@ for entry in node.get_entries(): _add_entry(entry) elif format in ('bzip2', 'gzip'): - info = tarfile.TarInfo(os.path.join(prefix, name)) - info.type = tarfile.REGTYPE - info.mtime = node.last_modified - info.size = node.content_length - archive.addfile(info, node.get_content()) + try: + info = tarfile.TarInfo(os.path.join(prefix, name)) + info.type = tarfile.REGTYPE + info.mtime = node.last_modified + info.size = node.content_length + archive.addfile(info, node.get_content()) + except tarfile.TarError, e: + raise Error, e else: # ZIP format - info = zipfile.ZipInfo(os.path.join(prefix, name)) - info.compress_type = zipfile.ZIP_DEFLATED - info.date_time = time.gmtime(node.last_modified)[:6] - info.file_size = node.content_length - archive.writestr(info, node.get_content().read()) + try: + info = zipfile.ZipInfo(os.path.join(prefix, name)) + info.compress_type = zipfile.ZIP_DEFLATED + info.date_time = time.gmtime(node.last_modified)[:6] + info.file_size = node.content_length + archive.writestr(info, node.get_content().read()) + except zipfile.error, e: + raise Error, e _add_entry(root) archive.close() @@ -98,22 +108,28 @@ format = name break if not format: - raise Exception, 'Unkown archive extension: %s' \ - % os.path.splitext(filename)[1] + raise Error, 'Unkown archive extension: %s' \ + % os.path.splitext(filename)[1] names = [] if format in ('bzip2', 'gzip'): - tar_file = tarfile.open(filename) - for tarinfo in tar_file: - names.append(tarinfo.name) - tar_file.extract(tarinfo, dest_path) + try: + tar_file = tarfile.open(filename) + for tarinfo in tar_file: + names.append(tarinfo.name) + tar_file.extract(tarinfo, dest_path) + except tarfile.TarError, e: + raise Error, e elif format == 'zip': - zip_file = zipfile.ZipFile(filename, 'r') - for name in zip_file.namelist(): - names.append(name) - path = os.path.join(dest_path, name) - if name.endswith('/'): - os.makedirs(path) - else: - file(path, 'wb').write(zip_file.read(name)) + try: + zip_file = zipfile.ZipFile(filename, 'r') + for name in zip_file.namelist(): + names.append(name) + path = os.path.join(dest_path, name) + if name.endswith('/'): + os.makedirs(path) + else: + file(path, 'wb').write(zip_file.read(name)) + except zipfile.error: + raise Error, e return os.path.commonprefix(names)