1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
# -*- coding: utf-8 -*-
# vim: noai:ts=4:sw=4:expandtab
import glob
import os.path
import platform
import shutil
from textwrap import dedent
# pylint: disable=redefined-builtin
from six.moves import input
from . import util
from .exception import BuildError, Error, YumError
from .trace_decorator import traceLog
def package_manager(config_opts, chroot, plugins):
pm = config_opts.get('package_manager', 'yum')
if pm == 'yum':
return Yum(config_opts, chroot, plugins)
elif pm == 'dnf':
(distribution, version) = platform.dist()[0:2]
if distribution in ['redhat', 'centos']:
version = int(version.split('.')[0])
if version < 8:
if 'dnf_warning' in config_opts and config_opts['dnf_warning']:
print("""WARNING! WARNING! WARNING!
You are building package for distribution which use DNF. However your system
does not support DNF. You can continue with YUM, which will likely succeed,
but the result may be little different.
You can suppress this warning when you put
config_opts['dnf_warning'] = False
in Mock config.""")
input("Press Enter to continue.")
return Yum(config_opts, chroot, plugins)
return Dnf(config_opts, chroot, plugins)
else:
# TODO specific exception type
raise Exception('Unrecognized package manager')
class _PackageManager(object):
name = None
command = None
builddep_command = None
resolvedep_command = None
@traceLog()
def __init__(self, config, buildroot, plugins):
self.config = config
self.plugins = plugins
self.buildroot = buildroot
@traceLog()
def build_invocation(self, *args):
invocation = []
common_opts = []
cmd = args[0]
if cmd == 'builddep':
args = args[1:]
invocation += self.builddep_command
common_opts = self.config[self.name + '_builddep_opts']
elif cmd == 'resolvedep':
if self.resolvedep_command:
args = args[1:]
invocation = self.resolvedep_command
else:
invocation = [self.command]
common_opts = self.config[self.name + '_common_opts']
else:
invocation = [self.command]
common_opts = self.config[self.name + '_common_opts']
invocation += ['--installroot', self.buildroot.make_chroot_path('')]
if cmd == 'upgrade' or cmd == 'update':
invocation += ['-y']
releasever = self.config['releasever']
if releasever:
invocation += ['--releasever', releasever]
if not self.config['online']:
invocation.append('-C')
if self.config['enable_disable_repos']:
invocation += self.config['enable_disable_repos']
invocation += common_opts
invocation += args
return invocation
@traceLog()
def execute(self, *args, **kwargs):
self.plugins.call_hooks("preyum")
env = self.config['environment'].copy()
env.update(util.get_proxy_environment(self.config))
env['LC_MESSAGES'] = 'C'
if self.buildroot.nosync_path:
env['LD_PRELOAD'] = self.buildroot.nosync_path
invocation = self.build_invocation(*args)
self.buildroot.root_log.debug(invocation)
kwargs['printOutput'] = kwargs.get('printOutput', True)
if not self.config['print_main_output']:
kwargs.pop('printOutput', None)
else:
kwargs['pty'] = kwargs.get('pty', True)
self.buildroot.nuke_rpm_db()
try:
out = util.do(invocation, env=env, **kwargs)
except Error as e:
raise YumError(str(e))
self.plugins.call_hooks("postyum")
return out
@traceLog()
# pylint: disable=unused-argument
def install(self, *args, **kwargs):
return self.execute('install', *args)
# pylint: disable=unused-argument
@traceLog()
def remove(self, *args, **kwargs):
return self.execute('remove', *args)
# pylint: disable=unused-argument
@traceLog()
def update(self, *args, **kwargs):
return self.execute('update', *args)
# pylint: disable=unused-argument
@traceLog()
def builddep(self, *args, **kwargs):
return self.execute('builddep', returnOutput=1, *args)
@traceLog()
def copy_gpg_keys(self):
pki_dir = self.buildroot.make_chroot_path('etc', 'pki', 'mock')
util.mkdirIfAbsent(pki_dir)
for pki_file in glob.glob("/etc/pki/mock/RPM-GPG-KEY-*"):
shutil.copy(pki_file, pki_dir)
def initialize(self):
self.copy_gpg_keys()
self.initialize_config()
def initialize_config(self):
raise NotImplementedError()
def replace_in_config(self, config_content):
""" expand resultdir in the yum.conf segment of the mock
configuration file.
"""
return config_content.replace(
"%(resultdir)s", self.config['resultdir'] % self.config)
def _check_command(self):
""" Check if main command exists """
if not os.path.exists(self.command):
raise Exception("""Command {0} is not available. Either install package containing this command
or run mock with --yum or --dnf to overwrite config value. However this may
lead to different dependency solving!""".format(self.command))
def check_yum_config(config, log):
if '\nreposdir' not in config:
log.warning(dedent("""\
reposdir option is not set in yum config. That means Yum/DNF
will use system-wide repos. To suppress that behavior, put
reposdir=/dev/null to your yum.conf in mock config.
"""))
class Yum(_PackageManager):
name = 'yum'
def __init__(self, config, buildroot, plugins):
super(Yum, self).__init__(config, buildroot, plugins)
self.command = config['yum_command']
self.builddep_command = [config['yum_builddep_command']]
self._check_command()
if os.path.exists('/usr/bin/yum-deprecated'):
self.resolvedep_command = [
'repoquery', '--resolve', '--requires',
'--config', self.buildroot.make_chroot_path('etc', 'yum', 'yum.conf')]
@traceLog()
def _write_plugin_conf(self, name):
""" Write 'name' file into pluginconf.d """
conf_path = self.buildroot.make_chroot_path('etc', 'yum', 'pluginconf.d', name)
with open(conf_path, 'w+') as conf_file:
conf_file.write(self.config[name])
@traceLog()
def initialize_config(self):
# use yum plugin conf from chroot as needed
pluginconf_dir = self.buildroot.make_chroot_path('etc', 'yum', 'pluginconf.d')
util.mkdirIfAbsent(pluginconf_dir)
config_content = self.config['yum.conf'].replace(
"plugins=1", dedent("""\
plugins=1
pluginconfpath={0}""".format(pluginconf_dir)))
config_content = self.replace_in_config(config_content)
check_yum_config(config_content, self.buildroot.root_log)
# write in yum.conf into chroot
# always truncate and overwrite (w+)
self.buildroot.root_log.debug('configure yum')
yumconf_path = self.buildroot.make_chroot_path('etc', 'yum', 'yum.conf')
with open(yumconf_path, 'w+') as yumconf_file:
yumconf_file.write(config_content)
# write in yum plugins into chroot
self.buildroot.root_log.debug('configure yum priorities')
self._write_plugin_conf('priorities.conf')
self.buildroot.root_log.debug('configure yum rhnplugin')
self._write_plugin_conf('rhnplugin.conf')
if self.config['subscription-manager.conf']:
self.buildroot.root_log.debug('configure RHSM rhnplugin')
self._write_plugin_conf('subscription-manager.conf')
pem_dir = self.buildroot.make_chroot_path('etc', 'pki', 'entitlement')
util.mkdirIfAbsent(pem_dir)
for pem_file in glob.glob("/etc/pki/entitlement/*.pem"):
shutil.copy(pem_file, pem_dir)
consumer_dir = self.buildroot.make_chroot_path('etc', 'pki', 'consumer')
util.mkdirIfAbsent(consumer_dir)
for consumer_file in glob.glob("/etc/pki/consumer/*.pem"):
shutil.copy(consumer_file, consumer_dir)
shutil.copy('/etc/rhsm/rhsm.conf',
self.buildroot.make_chroot_path('etc', 'rhsm'))
self.execute('repolist')
def install(self, *pkgs, **kwargs):
check = kwargs.pop('check', False)
if check:
out = self.execute('resolvedep', *pkgs, returnOutput=True,
printOutput=False, pty=False)
_check_missing(out)
out = super(Yum, self).install(*pkgs, **kwargs)
if check:
_check_missing(out)
def _check_missing(output):
for i, line in enumerate(output.split('\n')):
for msg in ('no package found for', 'no packages found for',
'missing dependency', 'error:'):
if msg in line.lower():
raise BuildError('\n'.join(output.split('\n')[i:]))
class Dnf(_PackageManager):
name = 'dnf'
def __init__(self, config, buildroot, plugins):
super(Dnf, self).__init__(config, buildroot, plugins)
self.command = config['dnf_command']
self.builddep_command = [self.command, 'builddep']
self._check_command()
self.resolvedep_command = ['repoquery', '--resolve', '--requires']
@traceLog()
def build_invocation(self, *args):
if 'dnf_builddep_opts' not in self.config:
self.config['dnf_builddep_opts'] = self.config['yum_builddep_opts']
if 'dnf_common_opts' not in self.config:
self.config['dnf_common_opts'] = self.config['yum_common_opts'] + ['--disableplugin=local',
'--setopt=deltarpm=false']
return super(Dnf, self).build_invocation(*args)
@traceLog()
def initialize_config(self):
if 'dnf.conf' in self.config:
config_content = self.config['dnf.conf']
else:
config_content = self.config['yum.conf']
config_content = self.replace_in_config(config_content)
check_yum_config(config_content, self.buildroot.root_log)
util.mkdirIfAbsent(self.buildroot.make_chroot_path('etc', 'dnf'))
dnfconf_path = self.buildroot.make_chroot_path('etc', 'dnf', 'dnf.conf')
with open(dnfconf_path, 'w+') as dnfconf_file:
dnfconf_file.write(config_content)
def builddep(self, *pkgs, **kwargs):
try:
super(Dnf, self).builddep(*pkgs, **kwargs)
except Error as e:
# pylint: disable=unused-variable
for i, line in enumerate(e.msg.split('\n')):
if 'no such command: builddep' in line.lower():
raise BuildError("builddep command missing.\nPlease install package dnf-plugins-core.")
raise
|