forked from sig_core/toolkit
attempt to build ISO in both mock and podman
This commit is contained in:
parent
43470e336b
commit
37c0be3fd0
@ -6,6 +6,7 @@ import glob
|
|||||||
import rpm
|
import rpm
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
|
import hashlib
|
||||||
|
|
||||||
# These are a bunch of colors we may use in terminal output
|
# These are a bunch of colors we may use in terminal output
|
||||||
class Color:
|
class Color:
|
||||||
@ -20,6 +21,47 @@ class Color:
|
|||||||
BOLD = '\033[1m'
|
BOLD = '\033[1m'
|
||||||
END = '\033[0m'
|
END = '\033[0m'
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
"""
|
||||||
|
Quick utilities that may be commonly used
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def get_checksum(path, hashtype, logger):
|
||||||
|
"""
|
||||||
|
Generates a checksum from the provided path by doing things in chunks.
|
||||||
|
This way we don't do it in memory.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
checksum = hashlib.new(hashtype)
|
||||||
|
except ValueError:
|
||||||
|
logger.error("Invalid hash type: %s" % hashtype)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
input_file = open(path, "rb")
|
||||||
|
except IOError as e:
|
||||||
|
logger.error("Could not open file %s: %s" % (path, e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
chunk = input_file.read(8192)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
checksum.update(chunk)
|
||||||
|
|
||||||
|
input_file.close()
|
||||||
|
stat = os.stat(path)
|
||||||
|
base = os.path.basename(path)
|
||||||
|
# This emulates our current syncing scripts that runs stat and
|
||||||
|
# sha256sum and what not with a very specific output.
|
||||||
|
return "%s: %s bytes\n%s (%s) = %s" % (
|
||||||
|
base,
|
||||||
|
stat.st_size,
|
||||||
|
hashtype.upper(),
|
||||||
|
base,
|
||||||
|
checksum.hexdigest()
|
||||||
|
)
|
||||||
|
|
||||||
# vars and additional checks
|
# vars and additional checks
|
||||||
rldict = {}
|
rldict = {}
|
||||||
sigdict = {}
|
sigdict = {}
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
- 'isomd5sum'
|
- 'isomd5sum'
|
||||||
- 'lorax-templates-rhel'
|
- 'lorax-templates-rhel'
|
||||||
- 'lorax-templates-generic'
|
- 'lorax-templates-generic'
|
||||||
|
- 'xorriso'
|
||||||
repoclosure_map:
|
repoclosure_map:
|
||||||
arches:
|
arches:
|
||||||
x86_64: '--forcearch=x86_64 --arch=x86_64 --arch=athlon --arch=i686 --arch=i586 --arch=i486 --arch=i386 --arch=noarch'
|
x86_64: '--forcearch=x86_64 --arch=x86_64 --arch=athlon --arch=i686 --arch=i586 --arch=i486 --arch=i386 --arch=noarch'
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
- 'isomd5sum'
|
- 'isomd5sum'
|
||||||
- 'lorax-templates-rhel'
|
- 'lorax-templates-rhel'
|
||||||
- 'lorax-templates-generic'
|
- 'lorax-templates-generic'
|
||||||
|
- 'xorriso'
|
||||||
repoclosure_map:
|
repoclosure_map:
|
||||||
arches:
|
arches:
|
||||||
x86_64: '--forcearch=x86_64 --arch=x86_64 --arch=athlon --arch=i686 --arch=i586 --arch=i486 --arch=i386 --arch=noarch'
|
x86_64: '--forcearch=x86_64 --arch=x86_64 --arch=athlon --arch=i686 --arch=i586 --arch=i486 --arch=i386 --arch=noarch'
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
- 'isomd5sum'
|
- 'isomd5sum'
|
||||||
- 'lorax-templates-rhel'
|
- 'lorax-templates-rhel'
|
||||||
- 'lorax-templates-generic'
|
- 'lorax-templates-generic'
|
||||||
|
- 'xorriso'
|
||||||
repoclosure_map:
|
repoclosure_map:
|
||||||
arches:
|
arches:
|
||||||
x86_64: '--forcearch=x86_64 --arch=x86_64 --arch=athlon --arch=i686 --arch=i586 --arch=i486 --arch=i386 --arch=noarch'
|
x86_64: '--forcearch=x86_64 --arch=x86_64 --arch=athlon --arch=i686 --arch=i586 --arch=i486 --arch=i386 --arch=noarch'
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
- 'libreport-rhel-anaconda-bugzilla'
|
- 'libreport-rhel-anaconda-bugzilla'
|
||||||
required_pkgs:
|
required_pkgs:
|
||||||
- 'lorax'
|
- 'lorax'
|
||||||
- 'genisoimage'
|
|
||||||
- 'isomd5sum'
|
- 'isomd5sum'
|
||||||
- 'lorax-templates-rhel'
|
- 'lorax-templates-rhel'
|
||||||
- 'lorax-templates-generic'
|
- 'lorax-templates-generic'
|
||||||
|
@ -39,6 +39,18 @@ fi
|
|||||||
|
|
||||||
# If we didn't fail, let's pack up everything!
|
# If we didn't fail, let's pack up everything!
|
||||||
cd "${MOCKBLD}"
|
cd "${MOCKBLD}"
|
||||||
|
|
||||||
|
# Get ISO manifest
|
||||||
|
if [ -f "/usr/bin/xorriso" ]; then
|
||||||
|
/usr/bin/xorriso -dev lorax/images/boot.iso --find |
|
||||||
|
tail -n+2 |
|
||||||
|
tr -d "'" |
|
||||||
|
cut -c2- sort >> lorax/images/boot.iso.manifest
|
||||||
|
elif [ -f "/usr/bin/isoinfo" ]; then
|
||||||
|
/usr/bin/isoinfo -R -f -i lorax/images/boot.iso |
|
||||||
|
grep -v '/TRANS.TBL$' | sort >> lorax/images/boot.iso.manifest
|
||||||
|
fi
|
||||||
|
|
||||||
tar czf "${LORAX_TAR}" lorax "${LOGFILE}"
|
tar czf "${LORAX_TAR}" lorax "${LOGFILE}"
|
||||||
|
|
||||||
tar_ret_val=$?
|
tar_ret_val=$?
|
||||||
|
@ -40,7 +40,7 @@ mock_ret_val=$?
|
|||||||
if [ $mock_ret_val -eq 0 ]; then
|
if [ $mock_ret_val -eq 0 ]; then
|
||||||
# Copy resulting data to /var/lib/mock/{{ shortname|lower }}-{{ major }}-{{ arch }}/result
|
# Copy resulting data to /var/lib/mock/{{ shortname|lower }}-{{ major }}-{{ arch }}/result
|
||||||
mkdir -p "${MOCK_RESL}"
|
mkdir -p "${MOCK_RESL}"
|
||||||
cp "${MOCK_CHRO}${BUILDDIR}/${IMAGE_ISO}" "${MOCK_RESL}"
|
cp "${MOCK_CHRO}${BUILDDIR}/${IMAGE_ISO}*" "${MOCK_RESL}"
|
||||||
else
|
else
|
||||||
echo "!! EXTRA ISO RUN FAILED !!"
|
echo "!! EXTRA ISO RUN FAILED !!"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -35,7 +35,7 @@ import productmd.treeinfo
|
|||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
from empanadas.common import Color, _rootdir
|
from empanadas.common import Color, _rootdir, Utils
|
||||||
|
|
||||||
class IsoBuild:
|
class IsoBuild:
|
||||||
"""
|
"""
|
||||||
@ -105,6 +105,10 @@ class IsoBuild:
|
|||||||
|
|
||||||
self.extra_files = rlvars['extra_files']
|
self.extra_files = rlvars['extra_files']
|
||||||
|
|
||||||
|
self.container = config['container']
|
||||||
|
if 'container' in rlvars and len(rlvars['container']) > 0:
|
||||||
|
self.container = rlvars['container']
|
||||||
|
|
||||||
self.staging_dir = os.path.join(
|
self.staging_dir = os.path.join(
|
||||||
config['staging_root'],
|
config['staging_root'],
|
||||||
config['category_stub'],
|
config['category_stub'],
|
||||||
@ -416,6 +420,8 @@ class IsoBuild:
|
|||||||
for variant in self.iso_map['images']:
|
for variant in self.iso_map['images']:
|
||||||
self._copy_lorax_to_variant(self.force_unpack, arch, variant)
|
self._copy_lorax_to_variant(self.force_unpack, arch, variant)
|
||||||
|
|
||||||
|
self._copy_boot_to_work(self.force_unpack, arch)
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
'[' + Color.BOLD + Color.GREEN + 'INFO' + Color.END + '] ' +
|
'[' + Color.BOLD + Color.GREEN + 'INFO' + Color.END + '] ' +
|
||||||
'Image variant phase completed'
|
'Image variant phase completed'
|
||||||
@ -595,25 +601,6 @@ class IsoBuild:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log.info('Copying %s boot iso to work directory...' % arch)
|
|
||||||
os.makedirs(iso_to_go, exist_ok=True)
|
|
||||||
|
|
||||||
rclevel = ''
|
|
||||||
if self.release_candidate:
|
|
||||||
rclevel = '-' + self.rclvl
|
|
||||||
|
|
||||||
isobootpath = '{}/{}-{}.{}{}-{}-{}.iso'.format(
|
|
||||||
iso_to_go,
|
|
||||||
self.shortname,
|
|
||||||
self.major_version,
|
|
||||||
self.minor_version,
|
|
||||||
rclevel,
|
|
||||||
arch,
|
|
||||||
image
|
|
||||||
)
|
|
||||||
|
|
||||||
shutil.copy2(src_to_image + '/images/boot.iso', isobootpath)
|
|
||||||
|
|
||||||
self.log.info('Copying base lorax to %s directory...' % image)
|
self.log.info('Copying base lorax to %s directory...' % image)
|
||||||
try:
|
try:
|
||||||
shutil.copytree(src_to_image, path_to_image, copy_function=shutil.copy2, dirs_exist_ok=True)
|
shutil.copytree(src_to_image, path_to_image, copy_function=shutil.copy2, dirs_exist_ok=True)
|
||||||
@ -630,6 +617,73 @@ class IsoBuild:
|
|||||||
'Cannot remove boot.iso'
|
'Cannot remove boot.iso'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _copy_boot_to_work(self, force_unpack, arch):
|
||||||
|
src_to_image = os.path.join(
|
||||||
|
self.lorax_work_dir,
|
||||||
|
arch,
|
||||||
|
'lorax'
|
||||||
|
)
|
||||||
|
|
||||||
|
iso_to_go = os.path.join(
|
||||||
|
self.iso_work_dir,
|
||||||
|
arch
|
||||||
|
)
|
||||||
|
|
||||||
|
path_to_src_image = '{}/{}'.format(
|
||||||
|
src_to_image,
|
||||||
|
'/images/boot.iso'
|
||||||
|
)
|
||||||
|
|
||||||
|
rclevel = ''
|
||||||
|
if self.release_candidate:
|
||||||
|
rclevel = '-' + self.rclvl
|
||||||
|
|
||||||
|
discname = '{}-{}.{}{}-{}-{}.iso'.format(
|
||||||
|
self.shortname,
|
||||||
|
self.major_version,
|
||||||
|
self.minor_version,
|
||||||
|
rclevel,
|
||||||
|
arch,
|
||||||
|
'boot'
|
||||||
|
)
|
||||||
|
|
||||||
|
isobootpath = '{}/{}'.format(
|
||||||
|
iso_to_go,
|
||||||
|
discname
|
||||||
|
)
|
||||||
|
|
||||||
|
manifest = '{}.{}'.format(
|
||||||
|
isobootpath,
|
||||||
|
'manifest'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not force_unpack:
|
||||||
|
file_check = isobootpath
|
||||||
|
if os.path.exists(file_check):
|
||||||
|
self.log.warn(
|
||||||
|
'[' + Color.BOLD + Color.YELLOW + 'WARN' + Color.END + '] ' +
|
||||||
|
'Boot image (' + discname + ') already exists'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.log.info('Copying %s boot iso to work directory...' % arch)
|
||||||
|
os.makedirs(iso_to_go, exist_ok=True)
|
||||||
|
shutil.copy2(path_to_src_image, isobootpath)
|
||||||
|
if os.path.exists(path_to_src_image + '.manifest'):
|
||||||
|
shutil.copy2(path_to_src_image + '.manifest', manifest)
|
||||||
|
|
||||||
|
self.log.info('Creating checksum for %s boot iso...' % arch)
|
||||||
|
checksum = Utils.get_checksum(isobootpath, self.checksum, self.log)
|
||||||
|
if not checksum:
|
||||||
|
self.log.error(
|
||||||
|
'[' + Color.BOLD + Color.RED + 'FAIL' + Color.END + '] ' +
|
||||||
|
isobootpath + ' not found! Are you sure we copied it?'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
with open(isobootpath + '.CHECKSUM', "w+") as c:
|
||||||
|
c.write(checksum)
|
||||||
|
c.close()
|
||||||
|
|
||||||
def run_boot_sync(self):
|
def run_boot_sync(self):
|
||||||
"""
|
"""
|
||||||
This unpacks into BaseOS/$arch/os, assuming there's no data actually
|
This unpacks into BaseOS/$arch/os, assuming there's no data actually
|
||||||
@ -866,9 +920,13 @@ class IsoBuild:
|
|||||||
)
|
)
|
||||||
raise SystemExit()
|
raise SystemExit()
|
||||||
|
|
||||||
|
if self.extra_iso_mode == 'podman':
|
||||||
|
self._extra_iso_podman_run(arches_to_build, images_to_build, work_root)
|
||||||
|
|
||||||
def _extra_iso_local_config(self, arch, image, grafts, work_root):
|
def _extra_iso_local_config(self, arch, image, grafts, work_root):
|
||||||
"""
|
"""
|
||||||
Local ISO build mode - this should build in mock
|
Local ISO build configuration - This generates the configuration for
|
||||||
|
both mock and podman entries
|
||||||
"""
|
"""
|
||||||
self.log.info('Generating Extra ISO configuration and script')
|
self.log.info('Generating Extra ISO configuration and script')
|
||||||
|
|
||||||
@ -997,6 +1055,114 @@ class IsoBuild:
|
|||||||
"""
|
"""
|
||||||
Runs the actual local process using mock
|
Runs the actual local process using mock
|
||||||
"""
|
"""
|
||||||
|
entries_dir = os.path.join(work_root, "entries")
|
||||||
|
extra_iso_cmd = '/bin/bash {}/extraisobuild-{}-{}.sh'.format(entries_dir, arch, image)
|
||||||
|
self.log.info('Starting mock build...')
|
||||||
|
p = subprocess.call(shlex.split(extra_iso_cmd))
|
||||||
|
if p != 0:
|
||||||
|
self.log.error('An error occured during execution.')
|
||||||
|
self.log.error('See the logs for more information.')
|
||||||
|
raise SystemExit()
|
||||||
|
# Copy it if the compose dir is here?
|
||||||
|
|
||||||
|
def _extra_iso_podman_run(self, arches, images, work_root):
|
||||||
|
"""
|
||||||
|
Does all the image building in podman containers to parallelize the
|
||||||
|
builds. This is a case where you can call this instead of looping mock,
|
||||||
|
or not run it in peridot. This gives the Release Engineer a little more
|
||||||
|
flexibility if they care enough.
|
||||||
|
|
||||||
|
This honestly assumes you are running this on a machine that has access
|
||||||
|
to the compose directories. It's the same as if you were doing a
|
||||||
|
reposync of the repositories.
|
||||||
|
"""
|
||||||
|
cmd = self.podman_cmd()
|
||||||
|
entries_dir = os.path.join(work_root, "entries")
|
||||||
|
for i in images:
|
||||||
|
entry_name_list = []
|
||||||
|
image_name = i
|
||||||
|
arch_sync = arches.copy()
|
||||||
|
|
||||||
|
for a in arch_sync:
|
||||||
|
entry_name = 'buildExtraImage-{}-{}.sh'.format(a, i)
|
||||||
|
entry_name_list.append(entry_name)
|
||||||
|
|
||||||
|
for pod in entry_name_list:
|
||||||
|
podman_cmd_entry = '{} run -d -it -v "{}:{}" -v "{}:{}" --name {} --entrypoint {}/{} {}'.format(
|
||||||
|
cmd,
|
||||||
|
self.compose_root,
|
||||||
|
self.compose_root,
|
||||||
|
entries_dir,
|
||||||
|
entries_dir,
|
||||||
|
pod,
|
||||||
|
entries_dir,
|
||||||
|
pod,
|
||||||
|
self.container
|
||||||
|
)
|
||||||
|
|
||||||
|
process = subprocess.call(
|
||||||
|
shlex.split(podman_cmd_entry),
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
|
||||||
|
join_all_pods = ' '.join(entry_name_list)
|
||||||
|
time.sleep(3)
|
||||||
|
self.log.info(
|
||||||
|
'[' + Color.BOLD + Color.GREEN + 'INFO' + Color.END + '] ' +
|
||||||
|
'Building ' + i + ' ...'
|
||||||
|
)
|
||||||
|
pod_watcher = '{} wait {}'.format(
|
||||||
|
cmd,
|
||||||
|
join_all_pods
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_man = subprocess.call(
|
||||||
|
shlex.split(pod_watcher),
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
|
||||||
|
# After the above is done, we'll check each pod process for an exit
|
||||||
|
# code.
|
||||||
|
pattern = "Exited (0)"
|
||||||
|
for pod in entry_name_list:
|
||||||
|
checkcmd = '{} ps -f status=exited -f name={}'.format(
|
||||||
|
cmd,
|
||||||
|
pod
|
||||||
|
)
|
||||||
|
podcheck = subprocess.Popen(
|
||||||
|
checkcmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
shell=True
|
||||||
|
)
|
||||||
|
|
||||||
|
output, errors = podcheck.communicate()
|
||||||
|
if 'Exited (0)' not in output.decode():
|
||||||
|
self.log.error(
|
||||||
|
'[' + Color.BOLD + Color.RED + 'FAIL' + Color.END + '] ' + pod
|
||||||
|
)
|
||||||
|
bad_exit_list.append(pod)
|
||||||
|
|
||||||
|
rmcmd = '{} rm {}'.format(
|
||||||
|
cmd,
|
||||||
|
join_all_pods
|
||||||
|
)
|
||||||
|
|
||||||
|
rmpod = subprocess.Popen(
|
||||||
|
rmcmd,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
shell=True
|
||||||
|
)
|
||||||
|
|
||||||
|
entry_name_list.clear()
|
||||||
|
self.log.info(
|
||||||
|
'[' + Color.BOLD + Color.GREEN + 'INFO' + Color.END + '] ' +
|
||||||
|
'Building ' + i + ' completed'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _generate_graft_points(
|
def _generate_graft_points(
|
||||||
self,
|
self,
|
||||||
@ -1471,6 +1637,23 @@ class IsoBuild:
|
|||||||
returned_cmd = ' '.join(cmd)
|
returned_cmd = ' '.join(cmd)
|
||||||
return returned_cmd
|
return returned_cmd
|
||||||
|
|
||||||
|
def podman_cmd(self) -> str:
|
||||||
|
"""
|
||||||
|
This generates the podman run command. This is in the case that we want
|
||||||
|
to do reposyncs in parallel as we cannot reasonably run multiple
|
||||||
|
instances of dnf reposync on a single system.
|
||||||
|
"""
|
||||||
|
cmd = None
|
||||||
|
if os.path.exists("/usr/bin/podman"):
|
||||||
|
cmd = "/usr/bin/podman"
|
||||||
|
else:
|
||||||
|
self.log.error('/usr/bin/podman was not found. Good bye.')
|
||||||
|
raise SystemExit("\n\n/usr/bin/podman was not found.\n\nPlease "
|
||||||
|
" ensure that you have installed the necessary packages on "
|
||||||
|
" this system. " + Color.BOLD + "Note that docker is not "
|
||||||
|
"supported." + Color.END
|
||||||
|
)
|
||||||
|
return cmd
|
||||||
|
|
||||||
class LiveBuild:
|
class LiveBuild:
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user