#!/usr/bin/env python
import os
import subprocess
import re
import sys
import configparser
import tempfile
from avocado import main
from avocado import Test
from moduleframework import module_framework
import cleanup
import brtconfig
class BaseRuntimeSetupDocker(module_framework.CommonFunctions, Test):
def setUp(self):
self.mockcfg = brtconfig.get_mockcfg(self)
self.br_image_name = brtconfig.get_docker_image_name(self)
def _process_mockcfg(self):
mockcfg = self.mockcfg
mock_root = ''
mockcfg_lines = []
#Regex to get packages that are configured on mockcfg to be installed
chroot_setup_pkg_regex = re.compile("config_opts\s*\[\s*'chroot_setup_cmd'\s*\]\s*="
"\s*'install --setopt=tsflags=nodocs\s*(.*)\s*'")
chroot_setup_pkgs = None
with open(mockcfg, 'r') as mock_cfgfile:
found_setup_cmd = False
for line in mock_cfgfile:
mockcfg_lines.append(line)
if re.match("config_opts\s*\[\s*'root'\s*\]", line) is not None:
mock_root = line.split('=')[1].split("'")[1]
if re.match("config_opts\s*\[\s*'chroot_setup_cmd'\s*\]", line) is not None:
found_setup_cmd = True
#Check if there are packages defined on chroot_setup_cmd
m = chroot_setup_pkg_regex.match(line)
if m:
chroot_setup_pkgs = sorted(m.group(1).split())
if len(mock_root) == 0:
self.error("mock configuration file %s does not specify mock root" %
mockcfg)
self.log.info("mock root: %s" % mock_root)
self.mock_root = mock_root
if not found_setup_cmd:
self.error("mock configuration file %s does not define chroot_setup_cmd" % mockcfg)
#Need to get all packages that need to be installed
mod_yaml = self.getModulemdYamlconfig()
if not mod_yaml:
self.error("Could not read modulemd Yaml file")
if "data" not in mod_yaml.keys():
self.error("'data' key was not found in modulemd Yaml file")
if "profiles" not in mod_yaml["data"].keys():
self.error("'profiles' key was not found in 'data' section")
if "container" not in mod_yaml["data"]["profiles"].keys():
self.error("'container' key was not found in 'profiles' section")
base_profile = mod_yaml["data"]["profiles"]["container"]
if "rpms" not in base_profile.keys():
self.error("'rpms' key was not found in 'container' profile")
req_pkgs = base_profile["rpms"]
if not req_pkgs:
self.error("Could not find any package to be installed in the image")
#Only update mockcfg if the list of packages changed
if cmp(chroot_setup_pkgs, sorted(req_pkgs)):
#Need to change chroot_setup_cmd line on mockcfg file
setup_cmd = "install --setopt=tsflags=nodocs "
setup_cmd += " ".join(req_pkgs)
with open(mockcfg, 'w') as mock_cfgfile:
for line in mockcfg_lines:
if re.match("config_opts\s*\[\s*'chroot_setup_cmd'\s*\]", line) is not None:
line = "config_opts['chroot_setup_cmd'] = '%s'\n" % setup_cmd
mock_cfgfile.write(line)
#Test will exit with WARN to inform the config file has changed
self.log.warning("List of packages to be installed by mock changed")
def _run_command(self, cmd):
try:
cmd_output = subprocess.check_output(
cmd, stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError as e:
self.error("command '%s' returned exit status %d; output:\n%s" %
(e.cmd, e.returncode, e.output))
else:
self.log.info("command '%s' succeeded with output:\n%s" %
(cmd, cmd_output))
def _configure_mock_microdnf(self):
"""
Configure mock chroot for microdnf so it carrys into the docker image
"""
# fetch the dnf.conf file from the mock chroot that was conveniently
# created based on the yum.conf value in the mock configuration file
tmpdnfcfg = tempfile.NamedTemporaryFile(delete=False)
self._run_command('mock -r %s --copyout /etc/dnf/dnf.conf %s' %
(self.mockcfg, tmpdnfcfg.name))
with open(tmpdnfcfg.name, 'r') as dnffile:
contents = dnffile.read()
self.log.info(
"Contents of original dnf.conf generated by mock:\n%s" % contents)
# load the dnf.conf file and remove the [main] section so only the
# repo section(s) remain
config = configparser.ConfigParser()
config.read(tmpdnfcfg.name)
self.log.info("Found the following configuration section(s): %s" %
' '.join(config.sections()))
if 'main' in config:
del config['main']
# write out the cleaned up repo configuration
tmpyumcfg = tempfile.NamedTemporaryFile(delete=False)
with open(tmpyumcfg.name, 'w') as repofile:
config.write(repofile, space_around_delimiters=False)
with open(tmpyumcfg.name, 'r') as yumfile:
contents = yumfile.read()
self.log.info("Contents of revised yum repo config:\n%s" % contents)
# copy the new yum repo configuration file into the mock chroot
self._run_command(
'mock -r %s --copyin %s /etc/yum.repos.d/build.repo' % (self.mockcfg, tmpyumcfg.name))
self._run_command(
'mock -r %s --chroot "chmod 644 /etc/yum.repos.d/build.repo"' % self.mockcfg)
# remove the temporary files
os.remove(tmpdnfcfg.name)
os.remove(tmpyumcfg.name)
# /etc/pki/rpm-gpg directory must exist or microdnf will explode
self._run_command(
'mock -r %s --chroot "mkdir -p -m=755 /etc/pki/rpm-gpg"' % self.mockcfg)
def testCreateDockerImage(self):
self._process_mockcfg()
# Clean-up any old test artifacts (docker containers, image, mock root)
# first:
try:
cleanup.cleanup_docker_and_mock(self.mockcfg, self.br_image_name)
except:
self.error("artifact cleanup failed")
else:
self.log.info("artifact cleanup successful")
# Initialize chroot with mock
self._run_command('mock -r %s --init' % self.mockcfg)
# Configure mock chroot for microdnf so it carrys into the docker image
self._configure_mock_microdnf()
# check if "sudo" allows us to tar up the chroot without a password
# Note: this must be configured in "sudoers" to work!
tar_cmd = "tar -C /var/lib/mock/%s/root -c ." % self.mock_root
try:
cmd_output = subprocess.check_output(
"sudo -n %s >/dev/null" % tar_cmd,
stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError as e:
# no luck using "sudo", warn and proceed as ordinary user without
# it
self.log.info("command '%s' returned exit status %d; output:\n%s" %
(e.cmd, e.returncode, e.output))
self.log.warning("NO SUDO RIGHTS TO RUN COMMAND '%s' AS ROOT" %
tar_cmd)
self.log.warning("GENERATED DOCKER IMAGE '%s' MAY BE INCOMPLETE!" %
self.br_image_name)
else:
# "sudo" works, so use it
tar_cmd = "sudo -n " + tar_cmd
# Import mock chroot as a docker image
self._run_command("%s | docker import - %s" %
(tar_cmd, self.br_image_name))
if __name__ == "__main__":
main()