diff --git a/vmupdate/agent/source/apt/apt_cli.py b/vmupdate/agent/source/apt/apt_cli.py index bbd5157..e6077f3 100644 --- a/vmupdate/agent/source/apt/apt_cli.py +++ b/vmupdate/agent/source/apt/apt_cli.py @@ -23,6 +23,7 @@ import fcntl import os +import contextlib from typing import List from source.common.package_manager import PackageManager @@ -38,12 +39,21 @@ def __init__(self, log_handler, log_level,): # to prevent a warning: `debconf: unable to initialize frontend: Dialog` os.environ['DEBIAN_FRONTEND'] = 'noninteractive' - def wait_for_lock(self): + @contextlib.contextmanager + def apt_lock(self): """ - Wait for any other apt-get instance to finish. + Contex manager for locking compatible with 'apt-get update' lock. """ with open("/var/lib/apt/lists/lock", "rb+") as f_lock: fcntl.lockf(f_lock.fileno(), fcntl.LOCK_EX) + yield + + def wait_for_lock(self): + """ + Wait for any other apt-get instance to finish. + """ + with self.apt_lock(): + pass def refresh(self, hard_fail: bool) -> ProcessResult: """ @@ -52,9 +62,12 @@ def refresh(self, hard_fail: bool) -> ProcessResult: :param hard_fail: raise error if some repo is unavailable :return: (exit_code, stdout, stderr) """ - self.wait_for_lock() - cmd = [self.package_manager, "-q", "update"] - result = self.run_cmd(cmd) + # apply lock externally to wait for it, until + # https://bugs.debian.org/1069167 gets implemented + with self.apt_lock(): + cmd = [self.package_manager, + "-o", "Debug::NoLocking=true", "-q", "update"] + result = self.run_cmd(cmd) # 'apt-get update' reports error with exit code 100, but updater as a # whole reserves it for "no updates" if result.code == 100: