Source code for pkgcore.operations.domain

"""
repository modifications (installing, removing, replacing)
"""

__all__ = ("Failure", "base", "install", "uninstall", "replace")

import shutil
import tempfile

from snakeoil import osutils
from snakeoil.dependant_methods import ForcedDepends

from ..exceptions import PkgcoreException
from ..log import logger
from ..merge import errors as merge_errors
from ..merge.engine import MergeEngine
from ..package.mutated import MutatedPkg


class fake_lock:
    def __init__(self):
        pass

    acquire_write_lock = acquire_read_lock = __init__
    release_read_lock = release_write_lock = __init__


[docs] class Failure(PkgcoreException): pass
[docs] class base(metaclass=ForcedDepends): stage_depends = {} stage_hooks = [] def __init__(self, domain, repo, observer, offset): self.domain = domain self.repo = repo self.underway = False self.offset = offset self.observer = observer self.triggers = self.domain.triggers self.create_op() self.lock = getattr(repo, "lock") self.tempspace = None if self.lock is None: self.lock = fake_lock()
[docs] def create_op(self): raise NotImplementedError(self, "create_op")
[docs] def create_repo_op(self): raise NotImplementedError(self, "create_repo_op")
[docs] def create_engine(self): raise NotImplementedError(self, "create_repo_op")
def _create_tempspace(self): location = self.domain.pm_tmpdir osutils.ensure_dirs(location) self.tempspace = tempfile.mkdtemp(dir=location, prefix="merge-engine-tmp") def _add_triggers(self, engine): for trigger in self.triggers: trigger.register(engine)
[docs] def customize_engine(self, engine): pass
[docs] def start(self): """start the transaction""" self._create_tempspace() self.me = engine = self.create_engine() self.format_op.add_triggers(self, engine) self._add_triggers(engine) self.customize_engine(engine) self.underway = True self.lock.acquire_write_lock() self.me.sanity_check() return True
[docs] def finish(self): """finish the transaction""" self.me.final() self.lock.release_write_lock() self.underway = False self.clean_tempdir() return True
[docs] def finalize_repo(self): """finalize the repository operations""" return self.repo_op.finish()
[docs] def clean_tempdir(self): if self.tempspace: try: shutil.rmtree(self.tempspace) except FileNotFoundError: pass self.tempspace = None return True
def _modify_repo_cache(self): raise NotImplementedError def __del__(self): if getattr(self, "underway", False): logger.warning(f"{self} merge was underway, but wasn't completed") self.lock.release_write_lock() self.clean_tempdir()
[docs] class install(base): """base interface for installing a pkg into a livefs repo. Repositories should override as needed. """ stage_depends = { "finish": "postinst", "postinst": "finalize_repo", "finalize_repo": "repo_add", "repo_add": "create_repo_op", "create_repo_op": "transfer", "transfer": "preinst", "preinst": "start", } stage_hooks = ["merge_metadata", "postinst", "preinst", "transfer"] format_install_op_name = "_repo_install_op" engine_kls = staticmethod(MergeEngine.install) def __init__(self, domain, repo, pkg, observer, offset): self.new_pkg = pkg super().__init__(domain, repo, observer, offset)
[docs] def create_op(self): self.format_op = getattr(self.new_pkg, self.format_install_op_name)( self.domain, self.observer )
def create_repo_op(self): self.repo_op = self.repo.operations.install(self.new_pkg, self.observer) return True
[docs] def create_engine(self): return self.engine_kls( self.tempspace, self.new_pkg, offset=self.offset, observer=self.observer )
def preinst(self): """execute any pre-transfer steps required""" return self.format_op.preinst() def _update_new_pkg(self, cset): self.new_pkg = MutatedPkg(self.new_pkg, {"contents": cset}) def transfer(self): """execute the actual transfer""" for merge_phase in (self.me.pre_merge, self.me.merge, self.me.post_merge): merge_phase() self._update_new_pkg(self.me.get_merged_cset()) return True def postinst(self): """execute any post-transfer steps required""" return self.format_op.postinst() def repo_add(self): return self.repo_op.add_data(self.domain) def finish(self): ret = self.format_op.finalize() if not ret: logger.warning(f"ignoring unexpected result from install finalize- {ret!r}") return base.finish(self)
[docs] class uninstall(base): """base interface for uninstalling a pkg from a livefs repo. Repositories should override as needed. """ stage_depends = { "finish": "postrm", "postrm": "finalize_repo", "finalize_repo": "repo_remove", "repo_remove": "remove", "remove": "prerm", "prerm": "create_repo_op", "create_repo_op": "start", } stage_hooks = ["merge_metadata", "postrm", "prerm", "remove"] format_uninstall_op_name = "_repo_uninstall_op" engine_kls = staticmethod(MergeEngine.uninstall) def __init__(self, domain, repo, pkg, observer, offset): self.old_pkg = pkg super().__init__(domain, repo, observer, offset)
[docs] def create_op(self): self.format_op = getattr(self.old_pkg, self.format_uninstall_op_name)( self.domain, self.observer )
def create_repo_op(self): self.repo_op = self.repo.operations.uninstall(self.old_pkg, self.observer) return True
[docs] def create_engine(self): return self.engine_kls( self.tempspace, self.old_pkg, offset=self.offset, observer=self.observer )
def prerm(self): """execute any pre-removal steps required""" return self.format_op.prerm() def remove(self): """execute any removal steps required""" for unmerge_phase in ( self.me.pre_unmerge, self.me.unmerge, self.me.post_unmerge, ): unmerge_phase() return True def postrm(self): """execute any post-removal steps required""" return self.format_op.postrm() def repo_remove(self): return self.repo_op.remove_data() def finish(self): ret = self.format_op.finalize() self.format_op.cleanup(disable_observer=True) if not ret: logger.warning( f"ignoring unexpected result from uninstall finalize- {ret!r}" ) return base.finish(self) def __del__(self): if getattr(self, "underway", False): logger.warning(f"{self.old_pkg} unmerge was underway, but wasn't completed") self.lock.release_write_lock()
[docs] class replace(install, uninstall): """base interface for replacing a pkg in a livefs repo with another. Repositories should override as needed. """ stage_depends = { "finish": "postinst", "postinst": "postrm", "postrm": "finalize_repo", "finalize_repo": "repo_remove", "repo_remove": "remove", "remove": "prerm", "prerm": "repo_add", "repo_add": "create_repo_op", "create_repo_op": "transfer", "transfer": "preinst", "preinst": "start", } stage_hooks = [ "merge_metadata", "unmerge_metadata", "postrm", "prerm", "postinst", "preinst", "unmerge_metadata", "merge_metadata", ] engine_kls = staticmethod(MergeEngine.replace) format_replace_op_name = "_repo_replace_op" def __init__(self, domain, repo, oldpkg, newpkg, observer, offset): self.old_pkg = oldpkg self.new_pkg = newpkg base.__init__(self, domain, repo, observer, offset)
[docs] def create_op(self): self.format_op = getattr(self.new_pkg, self.format_replace_op_name)( self.domain, self.old_pkg, self.observer ) return True
def create_repo_op(self): self.repo_op = self.repo.operations.replace( self.old_pkg, self.new_pkg, self.observer ) return True
[docs] def create_engine(self): return self.engine_kls( self.tempspace, self.old_pkg, self.new_pkg, offset=self.offset, observer=self.observer, )
def finish(self): ret = self.format_op.finalize() if not ret: logger.warning(f"ignoring unexpected result from replace finalize- {ret!r}") return base.finish(self) def __del__(self): if getattr(self, "underway", False): logger.warning( f"{self.old_pkg} -> {self.new_pkg} replacement was underway, but " "wasn't completed" ) self.lock.release_write_lock()