class SELinux(object): """On SELinux enabled box, this plugin will pretend, that SELinux is disabled in build environment. - fake /proc/filesystems is mounted into build environment, excluding selinuxfs - option '--setopt=tsflags=nocontext' is appended to each 'yum' command """ decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.conf = conf self.filesystems = self._selinuxCreateFauxFilesystems() self.chrootFilesystems = rootObj.makeChrootPath("/proc/filesystems") atexit.register(self._selinuxAtExit) self.rootObj.mounts.add( BindMountPoint(srcpath=self.filesystems, bindpath=self.chrootFilesystems)) if self._selinuxYumIsSetoptSupported(): rootObj.addHook("preyum", self._selinuxPreYumHook) rootObj.addHook("postyum", self._selinuxPostYumHook) else: getLog().warning( "selinux: 'yum' does not support '--setopt' option") decorate(traceLog()) def _selinuxCreateFauxFilesystems(self): (fd, path) = tempfile.mkstemp(prefix="mock-selinux-plugin.") host = open("/proc/filesystems") try: for line in host: if not "selinuxfs" in line: os.write(fd, line) finally: host.close() os.close(fd) os.chmod(path, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) return path decorate(traceLog()) def _selinuxAtExit(self): if os.path.exists(self.filesystems): try: os.unlink(self.filesystems) except OSError, e: getLog().warning( "unable to delete selinux filesystems (%s): %s" % (self.filesystems, e)) pass
def test_generator(self, captured): trace_decorator.traceLog()(exampleGenerator)() self.assertEqual(2, len(captured)) msg = captured[0][2][2] self.assertEqual("ENTER exampleGenerator()", msg) msg = captured[1][2][2] self.assertIn("LEAVE exampleGenerator --> <generator object exampleGenerator at ", msg)
def test_generator(self, captured): trace_decorator.traceLog()(exampleGenerator)() self.assertEqual(2, len(captured)) msg = captured[0][2][2] self.assertEqual('ENTER exampleGenerator()', msg) msg = captured[1][2][2] self.assertIn( 'LEAVE exampleGenerator --> <generator object exampleGenerator at ', msg)
class Tmpfs(object): """Mounts a tmpfs on the chroot dir""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.conf = conf self.maxSize = self.conf['max_fs_size'] self.mode = self.conf['mode'] self.optArgs = ['-o', 'mode=%s' % self.mode] if self.maxSize: self.optArgs += ['-o', 'size=' + self.maxSize] rootObj.addHook("preinit", self._tmpfsMount) rootObj.addHook("preshell", self._tmpfsMount) rootObj.addHook("prechroot", self._tmpfsMount) rootObj.addHook("postshell", self._tmpfsUmount) rootObj.addHook("postbuild", self._tmpfsUmount) rootObj.addHook("postchroot", self._tmpfsUmount) rootObj.addHook("initfailed", self._tmpfsUmount) getLog().info("tmpfs initialized") decorate(traceLog()) def _tmpfsMount(self): getLog().info("mounting tmpfs at %s." % self.rootObj.makeChrootPath()) mountCmd = ["mount", "-n", "-t", "tmpfs"] + self.optArgs + \ ["mock_chroot_tmpfs", self.rootObj.makeChrootPath()] mockbuild.util.do(mountCmd, shell=False) decorate(traceLog()) def _tmpfsUmount(self): force = False getLog().info("unmounting tmpfs.") umountCmd = ["umount", "-n", self.rootObj.makeChrootPath()] # since we're in a separate namespace, the mount will be cleaned up # on exit, so just warn if it fails here try: mockbuild.util.do(umountCmd, shell=False) except: getLog().warning( "tmpfs-plugin: exception while umounting tmpfs! (cwd: %s)" % os.getcwd()) force = True if force: # try umounting with force option umountCmd = ["umount", "-n", "-f", self.rootObj.makeChrootPath()] try: mockbuild.util.do(umountCmd, shell=False) except: getLog().warning( "tmpfs-plugin: exception while force umounting tmpfs! (cwd: %s)" % os.getcwd())
def test_exception(self, captured): try: trace_decorator.traceLog()(exampleRaising)('aaa') except MyException: pass self.assertEqual(3, len(captured)) msg, kw = captured[1][2][2], captured[1][3] self.assertTrue(kw['exc_info']) self.assertEquals("EXCEPTION: test exception\n", msg) msg = captured[2][2][2] self.assertEqual("LEAVE exampleRaising --> EXCEPTION RAISED\n", msg)
def test_exception(self, captured): try: trace_decorator.traceLog()(exampleRaising)("aaa") except MyException: pass self.assertEqual(3, len(captured)) msg, kw = captured[1][2][2], captured[1][3] self.assertTrue(kw["exc_info"]) self.assertEquals("EXCEPTION: test exception\n", msg) msg = captured[2][2][2] self.assertEqual("LEAVE exampleRaising --> EXCEPTION RAISED\n", msg)
def test_nested(self, captured): trace_decorator.traceLog()(exampleNested)(arg='ggg') self.assertEqual(4, len(captured)) msg = captured[0][2][2] self.assertEqual("ENTER exampleNested(arg='ggg')", msg) msg = captured[1][2][2] self.assertEqual("ENTER exampleFunc('bbb', arg='ggg')", msg) msg = captured[2][2][2] self.assertEqual("LEAVE exampleFunc --> 42\n", msg) msg = captured[3][2][2] self.assertEqual("LEAVE exampleNested --> 42\n", msg)
def test_nested(self, captured): trace_decorator.traceLog()(exampleNested)(arg="ggg") self.assertEqual(4, len(captured)) msg = captured[0][2][2] self.assertEqual("ENTER exampleNested(arg='ggg')", msg) msg = captured[1][2][2] self.assertEqual("ENTER exampleFunc('bbb', arg='ggg')", msg) msg = captured[2][2][2] self.assertEqual("LEAVE exampleFunc --> 42\n", msg) msg = captured[3][2][2] self.assertEqual("LEAVE exampleNested --> 42\n", msg)
class MountPoint(object): '''base class for mounts''' decorate(traceLog()) def __init__(self, mountsource, mountpath): self.mountpath = mountpath self.mountsource = mountsource decorate(traceLog()) def ismounted(self): if self.mountpath in [x.split()[1] for x in open('/proc/mounts')]: return True return False
class CCache(object): """enables ccache in buildroot/rpmbuild""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.ccache_opts = conf self.ccachePath = self.ccache_opts['dir'] % self.ccache_opts rootObj.ccacheObj = self rootObj.preExistingDeps.append("ccache") rootObj.addHook("prebuild", self._ccacheBuildHook) rootObj.addHook("preinit", self._ccachePreInitHook) rootObj.mounts.add( BindMountPoint(srcpath=self.ccachePath, bindpath=rootObj.makeChrootPath("/tmp/ccache"))) # ============= # 'Private' API # ============= # set the max size before we actually use it during a build. ccache itself # manages size and settings. we also set a few variables used by ccache to # find the shared cache. decorate(traceLog()) def _ccacheBuildHook(self): self.rootObj.doChroot( ["ccache", "-M", str(self.ccache_opts['max_cache_size'])], shell=False) # set up the ccache dir. # we also set a few variables used by ccache to find the shared cache. decorate(traceLog()) def _ccachePreInitHook(self): getLog().info("enabled ccache") envupd = {"CCACHE_DIR": "/tmp/ccache", "CCACHE_UMASK": "002"} if self.ccache_opts.get('compress') is not None: envupd["CCACHE_COMPRESS"] = str(self.ccache_opts['compress']) self.rootObj.env.update(envupd) mockbuild.util.mkdirIfAbsent( self.rootObj.makeChrootPath('/tmp/ccache')) mockbuild.util.mkdirIfAbsent(self.ccachePath) self.rootObj.uidManager.changeOwner(self.ccachePath)
class RootCache(object): """caches root environment in a tarball""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.root_cache_opts = conf self.rootSharedCachePath = self.root_cache_opts['dir'] % self.root_cache_opts self.rootCacheFile = os.path.join(self.rootSharedCachePath, "cache.tar") self.rootCacheLock = None self.compressProgram = self.root_cache_opts['compress_program'] if self.compressProgram == 'pigz' and not os.path.exists('/usr/bin/pigz'): getLog().warning("specified 'pigz' as the root cache compress program but not available; using gzip") self.compressProgram = 'gzip' if self.compressProgram: self.compressArgs = ['--use-compress-program', self.compressProgram] self.rootCacheFile = self.rootCacheFile + self.root_cache_opts['extension'] else: self.compressArgs = [] rootObj.rootCacheObj = self rootObj.addHook("preinit", self._rootCachePreInitHook) rootObj.addHook("preshell", self._rootCachePreShellHook) rootObj.addHook("prechroot", self._rootCachePreShellHook) rootObj.addHook("preyum", self._rootCachePreYumHook) rootObj.addHook("postinit", self._rootCachePostInitHook) rootObj.addHook("postshell", self._rootCachePostShellHook) rootObj.addHook("postchroot", self._rootCachePostShellHook) rootObj.addHook("postyum", self._rootCachePostShellHook) self.exclude_dirs = self.root_cache_opts['exclude_dirs'] self.exclude_tar_cmds = [ "--exclude=" + dir for dir in self.exclude_dirs] # ============= # 'Private' API # ============= decorate(traceLog()) def _rootCacheLock(self, shared=1): lockType = fcntl.LOCK_EX if shared: lockType = fcntl.LOCK_SH try: fcntl.lockf(self.rootCacheLock.fileno(), lockType | fcntl.LOCK_NB) except IOError, e: self.rootObj.start("Waiting for rootcache lock") fcntl.lockf(self.rootCacheLock.fileno(), lockType) self.rootObj.finish("Waiting for rootcache lock")
def test_capture(self, captured): trace_decorator.traceLog()(exampleFunc)('aaa') callLineno = getLineno() - 1 expectations = [(default_logger, logging.INFO, (filename, callLineno, "ENTER exampleFunc('aaa')"), { 'exc_info': None, 'args': [], 'func': 'test_capture' }), (default_logger, logging.INFO, (filename, getLineno(exampleFunc), "LEAVE exampleFunc --> 42\n"), { 'exc_info': None, 'args': [], 'func': 'exampleFunc' })] self.assertEqual(expectations, captured)
class PackageState(object): """dumps out a list of packages available and in the chroot""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.avail_done = False self.inst_done = False self.online = rootObj.online rootObj.addHook("postyum", self._availablePostYumHook) rootObj.addHook("prebuild", self._installedPreBuildHook) decorate(traceLog()) def _availablePostYumHook(self): if self.online and not self.avail_done: self.rootObj.uidManager.dropPrivsTemp() self.rootObj.start("Outputting list of available packages") out_file = self.rootObj.resultdir + '/available_pkgs' cmd = "/usr/bin/repoquery -c %s/etc/yum.conf %s > %s" % ( self.rootObj.makeChrootPath(), repoquery_avail_opts, out_file) mockbuild.util.do(cmd, shell=True) self.avail_done = True self.rootObj.finish("Outputting list of available packages") self.rootObj.uidManager.restorePrivs() decorate(traceLog()) def _installedPreBuildHook(self): if self.online and not self.inst_done: self.rootObj.start("Outputting list of installed packages") fd, fn = tempfile.mkstemp() fo = os.fdopen(fd, 'w') fo.write('[main]\ninstallroot=%s' % self.rootObj.makeChrootPath()) fo.flush() fo.close() out_file = self.rootObj.resultdir + '/installed_pkgs' cmd = "/usr/bin/repoquery -c %s %s > %s" % ( fn, repoquery_install_opts, out_file) mockbuild.util.do(cmd, shell=True, environ=self.rootObj.env) self.inst_done = True os.unlink(fn) self.rootObj.finish("Outputting list of installed packages")
def test_capture(self, captured): trace_decorator.traceLog()(exampleFunc)("aaa") callLineno = getLineno() - 1 expectations = [ ( default_logger, logging.INFO, (filename, callLineno, "ENTER exampleFunc('aaa')"), {"exc_info": None, "args": [], "func": "test_capture"}, ), ( default_logger, logging.INFO, (filename, getLineno(exampleFunc), "LEAVE exampleFunc --> 42\n"), {"exc_info": None, "args": [], "func": "exampleFunc"}, ), ] self.assertEqual(expectations, captured)
class BindMountPoint(MountPoint): '''class for managing bind-mounts in the chroot''' decorate(traceLog()) def __init__(self, srcpath, bindpath): MountPoint.__init__(self, mountsource=srcpath, mountpath=bindpath) self.srcpath = srcpath self.bindpath = bindpath self.mounted = self.ismounted() decorate(traceLog()) def mount(self): if not self.mounted: cmd = ['/bin/mount', '-n', '--bind', self.srcpath, self.bindpath] try: mockbuild.util.do(cmd) except mockbuild.exception.Error, e: return False self.mounted = True return True
class ChrootScan(object): """scan chroot for files of interest, copying to resultdir with relative paths""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.scan_opts = conf self.regexes = self.rootObj.pluginConf['chroot_scan_opts']['regexes'] self.resultdir = os.path.join(rootObj.resultdir, "chroot_scan") rootObj.scanObj = self rootObj.addHook("postbuild", self._scanChroot) getLog().info("chroot_scan: initialized") decorate(traceLog()) def _scanChroot(self): regexstr = "|".join(self.regexes) regex = re.compile(regexstr) chroot = self.rootObj.makeChrootPath() mockbuild.util.mkdirIfAbsent(self.resultdir) count = 0 logger = getLog() logger.debug("chroot_scan: Starting scan of %s" % chroot) copied = [] for root, dirs, files in os.walk(chroot): for f in files: m = regex.search(f) if m: srcpath = os.path.join(root, f) subprocess.call("cp --parents %s %s" % (srcpath, self.resultdir), shell=True) count += 1 copied.append(srcpath) logger.debug("chroot_scan: finished with %d files found" % count) if count: logger.info("chroot_scan: %d files copied to %s" % (count, self.resultdir)) logger.info("%s" % "\n".join(copied))
class YumCache(object): """caches root environment in a tarball""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.yum_cache_opts = conf self.yumSharedCachePath = self.yum_cache_opts[ 'dir'] % self.yum_cache_opts self.online = rootObj.online rootObj.yum_cacheObj = self rootObj.addHook("preyum", self._yumCachePreYumHook) rootObj.addHook("postyum", self._yumCachePostYumHook) rootObj.addHook("preinit", self._yumCachePreInitHook) rootObj.mounts.add( BindMountPoint(srcpath=self.yumSharedCachePath, bindpath=rootObj.makeChrootPath('/var/cache/yum'))) mockbuild.util.mkdirIfAbsent(self.yumSharedCachePath) self.yumCacheLock = open( os.path.join(self.yumSharedCachePath, "yumcache.lock"), "a+") # ============= # 'Private' API # ============= # lock the shared yum cache (when enabled) before any access # by yum, and prior to cleaning it. This prevents simultaneous access from # screwing things up. This can possibly happen, eg. when running multiple # mock instances with --uniqueext= decorate(traceLog()) def _yumCachePreYumHook(self): try: fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError, e: self.rootObj.start("Waiting for yumcache lock") fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX) self.rootObj.finish("Waiting for yumcache lock")
class Mount(object): """mount dirs into chroot""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.opts = conf rootObj.mountObj = self rootObj.addHook("preinit", self._mountPreInitHook) rootObj.addHook("preshell", self._mountPreInitHook) rootObj.addHook("prechroot", self._mountPreInitHook) for device, dest_dir, vfstype, mount_opts in self.opts['dirs']: if vfstype: vfstype = "-t " + vfstype else: vfstype = "" rootObj.mounts.add(FileSystemMountPoint(rootObj.makeChrootPath(dest_dir), filetype=vfstype, device=device, options=mount_opts)) decorate(traceLog()) def _mountPreInitHook(self): for device, dest_dir, vfstype, mount_opts in self.opts['dirs']: mockbuild.util.mkdirIfAbsent(self.rootObj.makeChrootPath(dest_dir))
class BindMount(object): """bind mount dirs from host into chroot""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.bind_opts = conf rootObj.bindMountObj = self rootObj.addHook("preinit", self._bindMountPreInitHook) rootObj.addHook("preshell", self._bindMountPreInitHook) rootObj.addHook("prechroot", self._bindMountPreInitHook) for srcdir, destdir in self.bind_opts['dirs']: rootObj.mounts.add( BindMountPoint(srcpath=srcdir, bindpath=rootObj.makeChrootPath(destdir))) decorate(traceLog()) def _bindMountPreInitHook(self): create_dirs = self.rootObj.pluginConf['bind_mount_opts']['create_dirs'] for srcdir, destdir in self.bind_opts['dirs']: if create_dirs: mockbuild.util.mkdirIfAbsent(srcdir) mockbuild.util.mkdirIfAbsent(self.rootObj.makeChrootPath(destdir))
class FileSystemMountPoint(MountPoint): '''class for managing filesystem mounts in the chroot''' decorate(traceLog()) def __init__(self, path, filetype=None, device=None, options=None): if not path: raise RuntimeError, "no path specified for mountpoint" if not filetype: raise RuntimeError, "no filetype specified for mountpoint" if filetype == 'pts' or filetype == 'proc' or filetype == 'sys': device = filetype if not device: raise RuntimeError, "no device file specified for mountpoint" MountPoint.__init__(self, mountsource=device, mountpath=path) self.device = device self.path = path self.filetype = filetype self.options = options self.mounted = self.ismounted() decorate(traceLog()) def mount(self): if self.mounted: return cmd = ['/bin/mount', '-n', '-t', self.filetype] if self.options: cmd += ['-o', self.options] cmd += [self.device, self.path] try: mockbuild.util.do(cmd) except mockbuild.exception.Error, e: return False self.mounted = True return True
class Mounts(object): '''class to manage all mountpoints''' decorate(traceLog()) def __init__(self, rootObj): self.rootObj = rootObj self.mounts = [ FileSystemMountPoint(filetype='proc', device='mock_chroot_proc', path=rootObj.makeChrootPath('/proc')), FileSystemMountPoint(filetype='sysfs', device='mock_chroot_sys', path=rootObj.makeChrootPath('/sys')), FileSystemMountPoint(filetype='tmpfs', device='mock_chroot_shmfs', path=rootObj.makeChrootPath('/dev/shm')), ] opts = 'gid=%d,mode=0620,ptmxmode=0666' % grp.getgrnam('tty').gr_gid if mockbuild.util.cmpKernelVer(os.uname()[2], '2.6.29') >= 0: opts += ',newinstance' self.mounts.append( FileSystemMountPoint(filetype='devpts', device='mock_chroot_devpts', path=rootObj.makeChrootPath('/dev/pts'), options=opts)) decorate(traceLog()) def add(self, mount): self.mounts.append(mount) decorate(traceLog()) def mountall(self): for m in self.mounts: m.mount() decorate(traceLog()) def umountall(self, force=False, nowarn=False): for m in reversed(self.mounts): m.umount() decorate(traceLog()) def get_mounted(self): return [m.mountpath for m in self.mounts if m.ismounted()] decorate(traceLog()) def get_mountpoints(self): return [m.mountpath for m in self.mounts]
class O0g3(object): """Source mount dirs from host into chroot""" decorate(traceLog()) def __init__(self, root, opts): self.root = root self.opts = opts self.O0g3s = { "gcc": "/usr/share/mock-O0g3-plugin/gcc-O0g3", "cc": "/usr/share/mock-O0g3-plugin/cc-O0g3", "g++": "/usr/share/mock-O0g3-plugin/g++-O0g3", "c++": "/usr/share/mock-O0g3-plugin/c++-O0g3", "strip": "/usr/share/mock-O0g3-plugin/strip-O0g3", } self.suffix = "O0g3" # See http://www.redhat.com/archives/rpm-list/2003-February/msg00174.html root.yumInstall("redhat-rpm-config") root.addHook("prebuild", self.prebuild) root.addHook("postbuild", self.postbuild) decorate(traceLog()) def prebuild(self): self.modifySpec() for k in self.O0g3s: self.replace(k) decorate(traceLog()) def postbuild(self): for k in self.O0g3s: self.revert(k) def modifySpec(self): getLog().info("Modify the spec file") root = self.root specs = glob.glob(root.makeChrootPath(root.builddir, "SPECS", "*.spec")) spec = specs[0] chrootspec = spec.replace(root.makeChrootPath(), '') # get rid of rootdir prefix root.doChroot( [ "sed", "-i", "-e", 's/^Release: .*/\\0.%s/' % (self.suffix), "-e", "1i %define debug_package %{nil}", "-e", "1i %define debug_packages %{nil}", "-e", "1i %define __strip :", chrootspec ], shell=False, logger=root.build_log, timeout=0, uid=root.chrootuid, gid=root.chrootgid, ) def makeOriginalPath(self, cmd): return self.root.makeChrootPath("/usr/bin" + "/" + cmd) def makeBackupPath(self, cmd): return self.root.makeChrootPath("/usr/bin" + "/" + "_" + cmd) def replace(self, cmd): getLog().info("Replace " + cmd) root = self.root original = self.makeOriginalPath(cmd) backup = self.makeBackupPath(cmd) f = open(original, mode='r') l = f.readline() f.close() if l != "#!/bin/bash": try: root.uidManager.becomeUser(0, 0) getLog().info("mv " + original + " " + backup) mockbuild.util.do(["/bin/mv", original, backup], shell=False) getLog().info("cp " + self.O0g3s[cmd] + " " + original) mockbuild.util.do(["/bin/cp", self.O0g3s[cmd], original], shell=False) finally: root.uidManager.restorePrivs() def revert(self, cmd): getLog().info("Revert " + cmd) root = self.root original = self.makeOriginalPath(cmd) backup = self.makeBackupPath(cmd) if os.path.exists(backup): try: root.uidManager.becomeUser(0, 0) getLog().info("mv " + backup + " " + original) mockbuild.util.do(["/bin/mv", backup, original], shell=False) finally: root.uidManager.restorePrivs()
class Root(object): """controls setup of chroot environment""" decorate(traceLog()) def __init__(self, config, uidManager): self._state = [] self.uidManager = uidManager self._hooks = {} self.chrootWasCached = False self.chrootWasCleaned = False self.preExistingDeps = [] self.logging_initialized = False self.buildrootLock = None self.version = config['version'] self.sharedRootName = config['root'] if config.has_key('unique-ext'): config['root'] = "%s-%s" % (config['root'], config['unique-ext']) self.basedir = os.path.join(config['basedir'], config['root']) self.rpmbuild_arch = config['rpmbuild_arch'] self._rootdir = os.path.join(self.basedir, 'root') self.homedir = config['chroothome'] self.builddir = os.path.join(self.homedir, 'build') # Environment self.env = config['environment'] # proxy settings for proto in ('http', 'https', 'ftp', 'no'): key = '%s_proxy' % proto value = config.get(key) if value: os.environ[key] = value self.env[key] = value # result dir self.resultdir = config['resultdir'] % config self.root_log = getLog("mockbuild") self.build_log = getLog("mockbuild.Root.build") self._state_log = getLog("mockbuild.Root.state") # config options self.configs = config['config_paths'] self.config_name = config['chroot_name'] self.chrootuid = config['chrootuid'] self.chrootuser = '******' self.chrootgid = config['chrootgid'] self.chrootgroup = 'mockbuild' self.yum_conf_content = config['yum.conf'] self.yum_priorities_conf_content = config['priorities.conf'] self.yum_rhnplugin_conf_content = config['rhnplugin.conf'] self.use_host_resolv = config['use_host_resolv'] self.chroot_file_contents = config['files'] self.chroot_setup_cmd = config['chroot_setup_cmd'] if isinstance(self.chroot_setup_cmd, basestring): # accept strings in addition to other sequence types self.chroot_setup_cmd = self.chroot_setup_cmd.split() self.yum_path = '/usr/bin/yum' self.yum_builddep_path = '/usr/bin/yum-builddep' self.yum_builddep_opts = config['yum_builddep_opts'] self.macros = config['macros'] self.more_buildreqs = config['more_buildreqs'] self.cache_topdir = config['cache_topdir'] self.cachedir = os.path.join(self.cache_topdir, self.sharedRootName) self.useradd = config['useradd'] self.online = config['online'] self.internal_dev_setup = config['internal_dev_setup'] self.plugins = config['plugins'] self.pluginConf = config['plugin_conf'] self.pluginDir = config['plugin_dir'] for key in self.pluginConf.keys(): if not key.endswith('_opts'): continue self.pluginConf[key]['basedir'] = self.basedir self.pluginConf[key]['cache_topdir'] = self.cache_topdir self.pluginConf[key]['cachedir'] = self.cachedir self.pluginConf[key]['root'] = self.sharedRootName # mount/umount self.mounts = mockbuild.mounts.Mounts(self) self.build_log_fmt_str = config['build_log_fmt_str'] self.root_log_fmt_str = config['root_log_fmt_str'] self._state_log_fmt_str = config['state_log_fmt_str'] self.start("init plugins") self._initPlugins() self.finish("init plugins") # do we allow interactive root shells? self.no_root_shells = config['no_root_shells'] # default to not doing selinux things self.selinux = False # if the selinux plugin is disabled and we have SELinux enabled # on the host, we need to do SELinux things, so set the selinux # state variable to true if self.pluginConf[ 'selinux_enable'] == False and mockbuild.util.selinuxEnabled(): self.selinux = True # ============= # 'Public' API # ============= decorate(traceLog()) def addHook(self, stage, function): hooks = self._hooks.get(stage, []) if function not in hooks: hooks.append(function) self._hooks[stage] = hooks decorate(traceLog()) def state(self): if not len(self._state): raise mockbuild.exception.StateError, "state called on empty state stack" return self._state[-1] def start(self, state): if state == None: raise mockbuild.exception.StateError, "start called with None State" self._state.append(state) self._state_log.info("Start: %s" % state) def finish(self, state): if len(self._state) == 0: raise mockbuild.exception.StateError, "finish called on empty state list" current = self._state.pop() if state != current: raise mockbuild.exception.StateError, "state finish mismatch: current: %s, state: %s" % ( current, state) self._state_log.info("Finish: %s" % state) def alldone(self): if len(self._state) != 0: raise mockbuild.exception.StateError, "alldone called with pending states: %s" % ",".join( self._state) decorate(traceLog()) def clean(self): """clean out chroot with extreme prejudice :)""" from signal import SIGKILL self.tryLockBuildRoot() self.start("clean chroot") self._callHooks('clean') mockbuild.util.orphansKill(self.makeChrootPath()) self._umountall(nowarn=True) self._unlock_and_rm_chroot() self.chrootWasCleaned = True self.finish("clean chroot") self.unlockBuildRoot() decorate(traceLog()) def _unlock_and_rm_chroot(self): if not os.path.exists(self.basedir): return t = self.basedir + ".tmp" if os.path.exists(t): mockbuild.util.rmtree(t, selinux=self.selinux) os.rename(self.basedir, t) self.buildrootLock.close() try: mockbuild.util.rmtree(t, selinux=self.selinux) except OSError, e: self.root_log.error(e) self.root_log.error("contents of /proc/mounts:\n%s" % open('/proc/mounts').read()) self.root_log.error("looking for users of %s" % t) self._show_path_user(t) raise self.root_log.info("chroot (%s) unlocked and deleted" % self.basedir)
def test_default_logger(self, captured): trace_decorator.traceLog()(exampleFunc)('aaa') loggers = [call[0] for call in captured] self.assertEqual([default_logger] * 2, loggers)
def exampleNested(**kwargs): return trace_decorator.traceLog()(exampleFunc)('bbb', **kwargs)
def test_custom_logger(self, captured): logger = type('custom_logger', (), {}) trace_decorator.traceLog(logger)(exampleFunc)('aaa') loggers = [call[0] for call in captured] self.assertEqual([logger] * 2, loggers)
class scmWorker(object): """Build RPMs from SCM""" decorate(traceLog()) def __init__(self, log, opts): self.log = log self.log.debug("Initializing SCM integration...") self.method = opts['method'] if self.method == "cvs": self.get = opts['cvs_get'] elif self.method == "svn": self.get = opts['svn_get'] elif self.method == "git": self.get = opts['git_get'] else: self.log.error("Unsupported SCM method: " + self.method) sys.exit(5) self.branch = None self.postget = None if 'branch' in opts: self.branch = opts['branch'] if self.branch: if self.method == "cvs": self.get = self.get.replace("SCM_BRN", "-r " + self.branch) elif self.method == "git": self.postget = "git checkout " + self.branch elif self.method == "svn": self.get = self.get.replace("SCM_BRN", self.branch) else: self.log.error("Unsupported SCM method: " + self.method) sys.exit(5) elif self.method == "svn": self.get = self.get.replace("SCM_BRN", "trunk") self.get = self.get.replace("SCM_BRN", "") if 'package' in opts: self.pkg = opts['package'] else: self.log.error("Trying to use SCM, package not defined") sys.exit(5) self.get = self.get.replace("SCM_PKG", self.pkg) self.spec = opts['spec'] self.spec = self.spec.replace("SCM_PKG", self.pkg) self.ext_src_dir = opts['ext_src_dir'] self.write_tar = opts['write_tar'] self.git_timestamps = opts['git_timestamps'] self.log.debug("SCM checkout command: " + self.get) self.log.debug("SCM checkout post command: " + str(self.postget)) self.environ = os.environ.copy() # Set HOME properly while checking out from SCM since tools like # Subversion might have there settings needed to carry out checkout # non-interactively self.environ['HOME'] = pwd.getpwuid(os.getuid()).pw_dir self.environ['CVS_RSH'] = "ssh" if not self.environ.has_key('SSH_AUTH_SOCK'): self.environ['SSH_AUTH_SOCK'] = pwd.getpwuid( os.getuid()).pw_dir + "/.ssh/auth_sock" decorate(traceLog()) def get_sources(self): self.wrk_dir = tempfile.mkdtemp(".mock-scm." + self.pkg) self.src_dir = self.wrk_dir + "/" + self.pkg self.log.debug("SCM checkout directory: " + self.wrk_dir) mockbuild.util.do(shlex.split(self.get), shell=False, cwd=self.wrk_dir, env=self.environ) if self.postget: mockbuild.util.do(shlex.split(self.postget), shell=False, cwd=self.src_dir, env=self.environ) self.log.debug("Fetched sources from SCM") decorate(traceLog()) def adjust_git_timestamps(self): dir = os.getcwd() self.log.debug("Adjusting timestamps in " + self.src_dir) os.chdir(self.src_dir) proc = subprocess.Popen(['git', 'ls-files', '-z'], shell=False, stdout=subprocess.PIPE) for f in proc.communicate()[0].split('\0')[:-1]: rev = subprocess.Popen( ['git', 'rev-list', 'HEAD', f], shell=False, stdout=subprocess.PIPE).stdout.readlines()[0].rstrip('\n') ts = subprocess.Popen( [ 'git', 'show', '--pretty=format:%ai', '--abbrev-commit', rev, f ], shell=False, stdout=subprocess.PIPE).stdout.readlines()[0].rstrip('\n') subprocess.Popen(['touch', '-d', ts, f], shell=False) os.chdir(dir) decorate(traceLog()) def prepare_sources(self): # import rpm after setarch import rpm self.log.debug("Preparing SCM sources") # Check some helper files if os.path.exists(self.src_dir + "/.write_tar"): self.log.debug( ".write_tar detected, will write tarball on the fly") self.write_tar = True # Figure out the spec file sf = self.src_dir + "/" + self.spec if not os.path.exists(sf): sf = self.src_dir + "/" + self.spec.lower() if not os.path.exists(sf): self.log.error("Can't find spec file %s" % self.src_dir + "/" + self.spec) self.clean() sys.exit(5) self.spec = sf # Dig out some basic information from the spec file self.sources = [] self.name = self.version = None ts = rpm.ts() rpm_spec = ts.parseSpec(self.spec) self.name = rpm.expandMacro("%{name}") self.version = rpm.expandMacro("%{version}") try: sources_list = rpm_spec.sources() except: sources_list = rpm_spec.sources for (filename, num, flags) in sources_list: self.sources.append(filename.split("/")[-1]) self.log.debug("Sources: %s" % self.sources) # Adjust timestamps for Git checkouts if self.method == "git" and self.git_timestamps: self.adjust_git_timestamps() # Generate a tarball from the checked out sources if needed if str(self.write_tar).lower() == "true": tardir = self.name + "-" + self.version tarball = tardir + ".tar.gz" taropts = "" proc = subprocess.Popen(['tar', '--help'], shell=False, stdout=subprocess.PIPE) if "--exclude-vcs" in proc.communicate()[0]: taropts = "--exclude-vcs" self.log.debug("Writing " + self.src_dir + "/" + tarball + "...") dir = os.getcwd() os.chdir(self.wrk_dir) os.rename(self.name, tardir) cmd = "tar czf " + tarball + " " + taropts + " " + tardir mockbuild.util.do(shlex.split(cmd), shell=False, cwd=self.wrk_dir, env=self.environ) os.rename(tarball, tardir + "/" + tarball) os.rename(tardir, self.name) os.chdir(dir) # Get possible external sources from an external sources directory for f in self.sources: if not os.path.exists(self.src_dir + "/" + f) and \ os.path.exists(self.ext_src_dir + "/" + f): self.log.debug("Copying " + self.ext_src_dir + "/" + f + " to " + self.src_dir + "/" + f) shutil.copy2(self.ext_src_dir + "/" + f, self.src_dir + "/" + f) self.log.debug("Prepared sources for building src.rpm") return (self.src_dir, self.spec) decorate(traceLog()) def clean(self): self.log.debug("Clean SCM checkout directory") mockbuild.util.rmtree(self.wrk_dir)
def test_custom_logger_fn(self, captured): logger = type("custom_logger", (), {}) trace_decorator.traceLog()(exampleFunc)("aaa", logger=logger) loggers = [call[0] for call in captured] self.assertEqual([logger] * 2, loggers)
def exampleNested(**kwargs): return trace_decorator.traceLog()(exampleFunc)("bbb", **kwargs)
def test_string_logger(self, captured): trace_decorator.traceLog('fake_logger')(exampleFunc)('aaa') loggers = [call[0] for call in captured] self.assertEqual(['fake_logger'] * 2, loggers)
class SourceRescue(object): """Rescuing rpmbuild -bp result""" decorate(traceLog()) def __init__(self, root, opts): self.result = -2 self.root = root self.opts = opts self.shelterdir = opts.get("shelterdir", False) or (root.resultdir + "/" + "srpmix") self.dont_make_patch_backup = opts.get("dont_make_patch_backup", True) self.salt = '.' + 'df6056a7-d1fc-4cc3-b831-6aac00e7f73a' if not self.shelterdir: raise RuntimeError, "Neither \"shelterdir\" config parameter nor \"resultdir\" config parameter given" if os.path.exists(self.shelterdir): raise RuntimeError, "%s already exists" % self.shelterdir root.addHook("prebuild", self.prebuild) root.addHook("postbuild", self.postbuild) decorate(traceLog()) def prebuild(self): root = self.root specs = glob.glob(root.makeChrootPath(root.builddir, "SPECS", "*.spec")) spec = specs[0] self.wash_spec(spec) getLog().info("Synthesizing source code") chrootspec = spec.replace(root.makeChrootPath(), '') # get rid of rootdir prefix try: root.doChroot([ "bash", "--login", "-c", 'rpmbuild -bp --target %s --nodeps %s' % (root.rpmbuild_arch, chrootspec) ], shell=False, logger=root.build_log, timeout=0, uid=root.chrootuid, gid=root.chrootgid, raiseExc=True) except: getLog().info("Failed in synthesizing") self.result = -1 raise SourceSOS getLog().info("Rescuing source code to %s" % self.shelterdir) bd_out = root.makeChrootPath(root.builddir) os.system("chmod -R a+r %s" % bd_out) os.system("find %s -type d -print0 | xargs -0 chmod a+x" % bd_out) shutil.copytree(bd_out, self.shelterdir, symlinks=True) os.system("find %s -name '*%s' -print0 | xargs -0 rm -f" % (self.shelterdir, self.salt)) os.system("mv %sb %s" % (spec, spec)) self.result = 0 raise SourceSOS decorate(traceLog()) def wash_spec(self, spec): if self.dont_make_patch_backup: self.wash__dont_make_patch_backup(spec) decorate(traceLog()) def wash__dont_make_patch_backup(self, spec): os.system("cp %s %sb" % (spec, spec)) sed = "sed -i -e 's/\\(^%%patch[0-9]\\+.*\\)[ \\t]-b[ \\t]\+[^ \\t]\+\\(.*\\)/\\1 -b %s \\2/' %s" os.system(sed % (self.salt, spec)) decorate(traceLog()) def postbuild(self): self.root.clean() sys.exit(self.result)
def test_default_logger(self, captured): trace_decorator.traceLog()(exampleFunc)("aaa") loggers = [call[0] for call in captured] self.assertEqual([default_logger] * 2, loggers)
config_opts['plugin_conf']['mount_opts']['dirs'].append(("/dev/device", "/mount/path/in/chroot/", "vfstype", "mount_options")) # A real life example: config_opts['plugin_conf']['mount_opts']['dirs'].append(("server.example.com:/exports/data", "/mnt/data", "nfs", "rw,hard,intr,nosuid,nodev,noatime,tcp")) """ import mockbuild.util from mockbuild.trace_decorator import decorate, traceLog from mockbuild.mounts import FileSystemMountPoint requires_api_version = "1.0" # plugin entry point decorate(traceLog()) def init(rootObj, conf): Mount(rootObj, conf) # classes class Mount(object): """mount dirs into chroot""" decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.opts = conf rootObj.mountObj = self rootObj.addHook("preinit", self._mountPreInitHook) rootObj.addHook("preshell", self._mountPreInitHook) rootObj.addHook("prechroot", self._mountPreInitHook)
def test_string_logger(self, captured): trace_decorator.traceLog("fake_logger")(exampleFunc)("aaa") loggers = [call[0] for call in captured] self.assertEqual(["fake_logger"] * 2, loggers)
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:textwidth=0: # License: GPL2 or later see COPYING # Written by Michael Brown # Copyright (C) 2007 Michael E Brown <*****@*****.**> # python library imports import os # our imports from mockbuild.trace_decorator import decorate, traceLog, getLog import mockbuild.util requires_api_version = "1.0" # plugin entry point decorate(traceLog()) def init(rootObj, conf): system_ram_bytes = os.sysconf(os.sysconf_names['SC_PAGE_SIZE']) * os.sysconf(os.sysconf_names['SC_PHYS_PAGES']) system_ram_mb = system_ram_bytes / (1024 * 1024) if system_ram_mb > conf['required_ram_mb']: Tmpfs(rootObj, conf) else: getLog().warning("Tmpfs plugin disabled. " "System does not have the required amount of RAM to enable the tmpfs plugin. " "System has %sMB RAM, but the config specifies the minimum required is %sMB RAM. " % (system_ram_mb, conf['required_ram_mb'])) # classes class Tmpfs(object): """Mounts a tmpfs on the chroot dir"""
class uidManager(object): decorate(traceLog()) def __init__(self, unprivUid=-1, unprivGid=-1): self.privStack = [] self.unprivUid = unprivUid self.unprivGid = unprivGid decorate(traceLog()) def becomeUser(self, uid, gid=-1): # save current ruid, euid, rgid, egid self._push() self._becomeUser(uid, gid) decorate(traceLog()) def dropPrivsTemp(self): # save current ruid, euid, rgid, egid self._push() self._becomeUser(self.unprivUid, self.unprivGid) decorate(traceLog()) def restorePrivs(self): # back to root first self._elevatePrivs() # then set saved privs = self.privStack.pop() os.setregid(privs['rgid'], privs['egid']) setresuid(privs['ruid'], privs['euid']) decorate(traceLog()) def dropPrivsForever(self): self._elevatePrivs() os.setregid(self.unprivGid, self.unprivGid) os.setreuid(self.unprivUid, self.unprivUid) decorate(traceLog()) def _push(self): # save current ruid, euid, rgid, egid self.privStack.append({ "ruid": os.getuid(), "euid": os.geteuid(), "rgid": os.getgid(), "egid": os.getegid(), }) decorate(traceLog()) def _elevatePrivs(self): setresuid(0, 0, 0) os.setregid(0, 0) decorate(traceLog()) def _becomeUser(self, uid, gid=None): self._elevatePrivs() if gid is not None: os.setregid(gid, gid) setresuid(uid, uid, 0) decorate(traceLog()) def changeOwner(self, path, uid=None, gid=None): self._elevatePrivs() if uid is None: uid = self.unprivUid if gid is None: gid = self.unprivGid os.chown(path, uid, gid)