Source code for mock_chroot

#!/usr/bin/env python
"""mock_chroot - Thin Python wrapper around mock(1)
"""
import os
from subprocess import check_output
from collections import Iterable
from tempfile import mkstemp

__all__ = ['MockChroot']


[docs]class MockChroot(object): """Create a Python wrapper object to magae *mock(1)* chroot environments :param str root: The name or path for the mock configuration file to use :param str config: The Mock configuration for the chroot as string or some other object that will yield a configuration string when passed to 'str()' 'root' and 'config' are mutually exclusive """ # Star arguments are used in this class # pylint: disable=star-args def __init__(self, root=None, config=None): """Create a mock(1) chroot """ if root and config: raise RuntimeError( "'root' and 'config' arguments are mutually exclusive" ) elif root: self._root = root elif config: (cfd, self._cfg_name) = mkstemp(suffix='.cfg') try: os.write(cfd, str(config)) finally: os.close(cfd) self._root = self._cfg_name else: # If no configuration specified, use the default file Mock would self._root = '/etc/mock/default.cfg' def __del__(self): try: os.remove(self._cfg_name) except AttributeError: pass
[docs] def dump_config(self): """Dump configuration for debugging purposes :returns: the current mock configuration as a string :rtype: str """ if hasattr(self, '_cfg_name'): dump = "Mock config from {0} follows".format(self._cfg_name) dump += '\n' + '-' * 78 with open(self._cfg_name, 'r') as ofd: dump += '\n' + ofd.read() dump += '\n' + '-' * 78 else: dump = "Using opaque Mock root string: {0}".format(self._root) return dump
[docs] def get_root_path(self): """Get the bash path of the chroot :returns: the bash path :rtype: str """ mock_cmd = self._mock_cmd('--print-root-path') output = check_output(mock_cmd) return output.rstrip()
[docs] def chroot(self, *cmd, **more_options): """Run a non-interactive command in mock All positional arguments are passes as the command to run and its argumens This method will behave in a similliar manner to subprocess.check_output yeilding CalledProcessError on command failure Optional named agruments passed via 'more_options' can be as follows: :param str cwd: Working directory inside the chroot to run in :returns: the command output as string :rtype: str """ mock_args = ['--chroot'] if 'cwd' in more_options: mock_args.extend(('--cwd', more_options['cwd'])) if 'resultdir' in more_options: mock_args.extend(('--resultdir', more_options['resultdir'])) mock_args.append('--') mock_args.extend(cmd) mock_cmd = self._mock_cmd(*mock_args) output = check_output(mock_cmd) return output
[docs] def clean(self): """Clean the mock chroot """ mock_cmd = self._mock_cmd('--clean') check_output(mock_cmd)
[docs] def rebuild(self, src_rpm, no_clean=False, define=None, resultdir=None): """Build a package from .src.rpm in Mock :param str src_rpm: The path to the .src.rpm file to build :param bool no_clean: Avoid cleaning the chroot before building :param object define: An optional define string for the build process or an Iterable of multiple such define strings. :param str resultdir: Override where the build results get placed :returns: the command output as string :rtype: str """ options = self._setup_mock_build_options( no_clean=no_clean, define=define, resultdir=resultdir ) if self.has_dnf(): # On platforms with dnf we must use it for build requirement # resolution but we cannot use it for initializing the chroot so # we must pre-init init_mock_cmd = self._mock_cmd('--init', *options) check_output(init_mock_cmd) # Force --no-clean so our initialization will not be destroyed options = self._setup_mock_build_options( no_clean=True, define=define, resultdir=resultdir ) options += ('--dnf',) mock_cmd = self._mock_cmd('--rebuild', src_rpm, *options) output = check_output(mock_cmd) return output
[docs] def buildsrpm( # pylint: disable=too-many-arguments,bad-continuation self, spec, sources, no_clean=False, define=None, resultdir=None ): """Build a .src.rpm package from sources and spcefile in Mock :param str spec: The path to the specfile to build :param str sources: The path to the sources directory :param bool no_clean: Avoid cleaning the chroot before building :param object define: An optional define string for the build process or an Iterable of multiple such define strings. :param str resultdir: Override where the build results get placed :returns: the command output as string :rtype: str """ options = self._setup_mock_build_options( no_clean=no_clean, define=define, resultdir=resultdir ) if self.has_dnf(): # On platforms with dnf we must use it for build requirement # resolution but we cannot use it for initializing the chroot so # we must pre-init init_mock_cmd = self._mock_cmd('--init', *options) check_output(init_mock_cmd) # Force --no-clean so our initialization will not be destroyed options = self._setup_mock_build_options( no_clean=True, define=define, resultdir=resultdir ) options += ('--dnf',) mock_cmd = self._mock_cmd( '--buildsrpm', '--spec', spec, '--sources', sources, *options ) output = check_output(mock_cmd) return output
def _mock_cmd(self, *more_args): """Create the Mock command line """ cmd = [self.mock_exe(), '--root={}'.format(self._root)] cmd.extend(more_args) return tuple(cmd) @staticmethod def _setup_mock_build_options(no_clean=False, define=None, resultdir=None): """Setup common options for the various RPM building commands :param bool no_clean: Avoid cleaning the chroot before building :param object define: An optional define string for the build process or an Iterable of multiple such define strings. :param str resultdir: Override where the build results get placed :returns: A tuple of Mock command line arguments for the given options :rtype: tuple """ options = [] if no_clean: options.append('--no-clean',) if define: if isinstance(define, basestring): options.extend(('--define', define)) elif isinstance(define, Iterable): for a_define in define: options.extend(('--define', a_define)) else: raise TypeError( "given 'define' is not an Iterable or a string" ) if resultdir: options.extend(('--resultdir', resultdir)) return options @staticmethod
[docs] def mock_exe(): """Returns the full path to the Mock executable """ return '/usr/bin/mock'
@classmethod
[docs] def has_dnf(cls): """Returns true if dnf is installed """ try: return cls._has_dnf except AttributeError: dnf = '/usr/bin/dnf' cls._has_dnf = os.path.exists(dnf) and os.access(dnf, os.X_OK) return cls._has_dnf