From 8b56c225b1ebfe00a60997d65dc5532bf4246db6 Mon Sep 17 00:00:00 2001 From: Matthew Johnson Date: Wed, 11 Oct 2017 14:01:18 -0700 Subject: [PATCH] Create mock chroot with a unique uniqueext This prevents collisions when two instances are trying to build the same package in parallel. Unit tests added as well. Signed-off-by: Matthew Johnson --- autospec/build.py | 50 +++++++++++++++++++++++++++++++++++++++++---- tests/test_build.py | 27 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/autospec/build.py b/autospec/build.py index e75edb9..93d5afa 100644 --- a/autospec/build.py +++ b/autospec/build.py @@ -25,6 +25,7 @@ import tarball import os import grp import shutil +import subprocess import config import util @@ -36,6 +37,7 @@ base_path = None output_path = None download_path = None mock_cmd = '/usr/bin/mock' +uniqueext = '' def setup_workingdir(workingdir): @@ -164,6 +166,36 @@ def parse_build_results(filename, returncode, filemanager): success = 1 +def reserve_path(path): + try: + subprocess.check_output(['sudo', 'mkdir', path], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + out = err.output.decode('utf-8') + return not "File exists" in out + + return True + + +def get_uniqueext(dirn, dist, name): + """ + Find a unique name to create mock chroot without reusing an old one + """ + # Default to tarball name + resultsdir = os.path.join(dirn, "{}-{}".format(dist, name)) + if reserve_path(resultsdir): + return name + + # Find a unique extension by checking if it exists in /var/lib/mock + # Increment the pathname until an unused path is found + resultsdir += "-1" + seq = 1 + while not reserve_path(resultsdir): + seq += 1 + resultsdir = resultsdir.replace("-{}".format(seq - 1), "-{}".format(seq)) + + return "{}-{}".format(name, seq) + + def set_mock(): global mock_cmd # get group list of current user @@ -175,18 +207,28 @@ def set_mock(): def package(filemanager): global round + global uniqueext round = round + 1 set_mock() print("Building package " + tarball.name + " round", round) - # call(mock_cmd + " -q -r clear --scrub=cache") - # call(mock_cmd + " -q -r clear --scrub=all") + + # determine uniqueext only once + uniqueext = uniqueext or get_uniqueext("/var/lib/mock", "clear", tarball.name) + print("{} mock chroot at /var/lib/mock/clear-{}".format(tarball.name, uniqueext)) + shutil.rmtree('{}/results'.format(download_path), ignore_errors=True) os.makedirs('{}/results'.format(download_path)) - util.call(mock_cmd + " -r clear --buildsrpm --sources=./ --spec={0}.spec --uniqueext={0} --result=results/ --no-cleanup-after".format(tarball.name), + util.call("{} -r clear --buildsrpm --sources=./ --spec={}.spec " + "--uniqueext={} --result=results/ --no-cleanup-after" + .format(mock_cmd, tarball.name, uniqueext), logfile="%s/mock_srpm.log" % download_path, cwd=download_path) + util.call("rm -f results/build.log", cwd=download_path) srcrpm = "results/%s-%s-%s.src.rpm" % (tarball.name, tarball.version, tarball.release) - returncode = util.call(mock_cmd + " -r clear --result=results/ %s --enable-plugin=ccache --uniqueext=%s --no-cleanup-after" % (srcrpm, tarball.name), + returncode = util.call("{} -r clear --result=results/ {} " + "--enable-plugin=ccache --uniqueext={} " + "--no-cleanup-after" + .format(mock_cmd, srcrpm, uniqueext), logfile="%s/mock_build.log" % download_path, check=False, cwd=download_path) # sanity check the build log if not os.path.exists(download_path + "/results/build.log"): diff --git a/tests/test_build.py b/tests/test_build.py index e32aa20..8ba53d3 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -1,4 +1,6 @@ import unittest +import tempfile +import os from unittest.mock import patch, mock_open import build import files @@ -376,6 +378,31 @@ class TestBuildpattern(unittest.TestCase): self.assertEqual(build.mock_cmd, '/usr/bin/mock') + def test_get_uniqueext_first(self): + """ + Test get_uniqueext() with no collisions + """ + with tempfile.TemporaryDirectory() as tmpd: + self.assertEqual(build.get_uniqueext(tmpd, "test", "pkg"), "pkg") + + def test_get_uniqueext_second(self): + """ + Test get_uniqueext() with one collision + """ + with tempfile.TemporaryDirectory() as tmpd: + os.mkdir(os.path.join(tmpd, "test-pkg")) + self.assertEqual(build.get_uniqueext(tmpd, "test", "pkg"), "pkg-1") + + def test_get_uniqueext_third(self): + """ + Test get_uniqueext() with two collisions + """ + with tempfile.TemporaryDirectory() as tmpd: + os.mkdir(os.path.join(tmpd, "test-pkg")) + os.mkdir(os.path.join(tmpd, "test-pkg-1")) + self.assertEqual(build.get_uniqueext(tmpd, "test", "pkg"), "pkg-2") + + if __name__ == '__main__': unittest.main(buffer=True)