|
# This file is part of Buildbot. Buildbot is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, version 2. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright Buildbot Team Members
except ImportError: from StringIO import StringIO
""" Helper class that acts as a file-object with write access """
# Create missing directories. os.makedirs(dirname)
""" Called from remote slave to write L{data} to L{fp} within boundaries of L{maxsize}
@type data: C{string} @param data: String of data to write """ if len(data) > self.remaining: data = data[:self.remaining] self.fp.write(data) self.remaining = self.remaining - len(data) else:
""" Called by remote slave to state that no more data will be transfered """ # on windows, os.rename does not automatically unlink, so do it manually
# unclean shutdown, the file is probably truncated, so delete it # altogether rather than deliver a corrupted file fp = getattr(self, "fp", None) if fp: fp.close() os.unlink(self.destfile) if self.tmpname and os.path.exists(self.tmpname): os.unlink(self.tmpname)
"""Fallback extractall method for TarFile, in case it doesn't have its own."""
import copy
directories = []
if members is None: members = self
for tarinfo in members: if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 0700 self.extract(tarinfo, path)
# Reverse sort directories. directories.sort(lambda a, b: cmp(a.name, b.name)) directories.reverse()
# Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) try: self.chown(tarinfo, dirpath) self.utime(tarinfo, dirpath) self.chmod(tarinfo, dirpath) except tarfile.ExtractError, e: if self.errorlevel > 1: raise else: self._dbg(1, "tarfile: %s" % e)
""" A DirectoryWriter is implemented as a FileWriter, with an added post-processing step to unpack the archive, once the transfer has completed. """
""" Called by remote slave to state that no more data will be transfered """ # Make sure remote_close is called, otherwise atomic rename wont happen
# Map configured compression to a TarFile setting mode='r|bz2' mode='r|gz' else:
# Support old python tarfile.TarFile.extractall = _extractall
# Unpack archive and clean up after self
""" Base class for FileUpload and FileDownload to factor out common functionality. """
else:
self.addCompleteLog('interrupt', str(reason)) if self.cmd: d = self.cmd.interrupt(reason) return d
# Subclasses may choose to skip a transfer. In those cases, self.cmd # will be None, and we should just let BuildStep.finished() handle # the rest if result == SKIPPED: return BuildStep.finished(self, SKIPPED)
if self.cmd.rc is None or self.cmd.rc == 0: return BuildStep.finished(self, SUCCESS) return BuildStep.finished(self, FAILURE)
workdir=None, maxsize=None, blocksize=16*1024, mode=None, keepstamp=False, url=None, **buildstep_kwargs):
'mode must be an integer or None')
m = "slave is too old, does not know about uploadFile" raise BuildSlaveTooOldError(m)
# we rely upon the fact that the buildmaster runs chdir'ed into its # basedir to make sure that relative paths in masterdest are expanded # properly. TODO: maybe pass the master's basedir all the way down # into the BuildStep so we can do this better. % (source, masterdest))
# we use maxsize to limit the amount of data on both sides
m = ("This buildslave (%s) does not support preserving timestamps. " "Please upgrade the buildslave." % self.build.slavename ) raise BuildSlaveTooOldError(m)
# default arguments 'slavesrc': source, 'workdir': self._getWorkdir(), 'writer': fileWriter, 'maxsize': self.maxsize, 'blocksize': self.blocksize, 'keepstamp': self.keepstamp, }
def cancel(res): fileWriter.cancel() return res
workdir=None, maxsize=None, blocksize=16*1024, compress=None, url=None, **buildstep_kwargs):
config.error( "'compress' must be one of None, 'gz', or 'bz2'")
m = "slave is too old, does not know about uploadDirectory" raise BuildSlaveTooOldError(m)
# we rely upon the fact that the buildmaster runs chdir'ed into its # basedir to make sure that relative paths in masterdest are expanded # properly. TODO: maybe pass the master's basedir all the way down # into the BuildStep so we can do this better. % (source, masterdest))
self.addURL(os.path.basename(masterdest), self.url)
# we use maxsize to limit the amount of data on both sides
# default arguments 'slavesrc': source, 'workdir': self._getWorkdir(), 'writer': dirWriter, 'maxsize': self.maxsize, 'blocksize': self.blocksize, 'compress': self.compress }
def cancel(res): dirWriter.cancel() return res
# Subclasses may choose to skip a transfer. In those cases, self.cmd # will be None, and we should just let BuildStep.finished() handle # the rest return BuildStep.finished(self, SKIPPED)
return BuildStep.finished(self, FAILURE)
""" Helper class that acts as a file-object with read access """
""" Called from remote slave to read at most L{maxlength} bytes of data
@type maxlength: C{integer} @param maxlength: Maximum number of data bytes that can be returned
@return: Data read from L{fp} @rtype: C{string} of bytes read from file """ return ''
""" Called by remote slave to state that no more data will be transfered """ if self.fp is not None: self.fp.close() self.fp = None
workdir=None, maxsize=None, blocksize=16*1024, mode=None, **buildstep_kwargs): BuildStep.__init__(self, **buildstep_kwargs)
self.mastersrc = mastersrc self.slavedest = slavedest self.workdir = workdir self.maxsize = maxsize self.blocksize = blocksize if not isinstance(mode, (int, type(None))): config.error( 'mode must be an integer or None') self.mode = mode
version = self.slaveVersion("downloadFile") if not version: m = "slave is too old, does not know about downloadFile" raise BuildSlaveTooOldError(m)
# we are currently in the buildmaster's basedir, so any non-absolute # paths will be interpreted relative to that source = os.path.expanduser(self.mastersrc) slavedest = self.slavedest log.msg("FileDownload started, from master %r to slave %r" % (source, slavedest))
self.step_status.setText(['downloading', "to", os.path.basename(slavedest)])
# setup structures for reading the file try: fp = open(source, 'rb') except IOError: # if file does not exist, bail out with an error self.addCompleteLog('stderr', 'File %r not available at master' % source) # TODO: once BuildStep.start() gets rewritten to use # maybeDeferred, just re-raise the exception here. reactor.callLater(0, BuildStep.finished, self, FAILURE) return fileReader = _FileReader(fp)
# default arguments args = { 'slavedest': slavedest, 'maxsize': self.maxsize, 'reader': fileReader, 'blocksize': self.blocksize, 'workdir': self._getWorkdir(), 'mode': self.mode, }
self.cmd = makeStatusRemoteCommand(self, 'downloadFile', args) d = self.runCommand(self.cmd) d.addCallback(self.finished).addErrback(self.failed)
workdir=None, maxsize=None, blocksize=16*1024, mode=None, **buildstep_kwargs):
config.error( 'mode must be an integer or None')
m = "slave is too old, does not know about downloadFile" raise BuildSlaveTooOldError(m)
# we are currently in the buildmaster's basedir, so any non-absolute # paths will be interpreted relative to that
os.path.basename(slavedest)])
# setup structures for reading the file
# default arguments 'slavedest': slavedest, 'maxsize': self.maxsize, 'reader': fileReader, 'blocksize': self.blocksize, 'workdir': self._getWorkdir(), 'mode': self.mode, }
del buildstep_kwargs['s']
del buildstep_kwargs['s']
properties=props, sourcestamp=self.build.getSourceStamp().asDict(), ), ) |