import tempfile
from contextlib import ExitStack
from pathlib import Path
from exasol.ansible.access import Access
from exasol.ansible.repository import (
Asset,
Repository,
)
from exasol.ansible.runner import Runner
class FilenameConflict(RuntimeError):
"""
Signals duplicate filenames or files vs. directories when using
multiple instances of Repository.
"""
class AssetCopier:
def __init__(self, relative_target: Path):
self.target = relative_target
self._seen: dict[Path, str] = {}
def copy(self, asset: Asset) -> None:
for path, path_type in asset.paths().items():
if (existing := self._seen.get(path)) is not None:
if existing == path_type == "file":
raise FilenameConflict(f"Duplicate file detected: {path}")
raise FilenameConflict(f"Path collision detected: {path}")
self._seen[path] = path_type
asset.copy_to(self.target)
[docs]
class Context:
"""
Create a temporary Ansible execution context from the given repositories.
Args:
ansible_access: Access configuration used by the created
``Runner``.
repositories: Repositories whose assets are copied into the temporary
execution directory.
work_dir: Optional working directory to use instead of creating a new
temporary directory.
"""
def __init__(
self,
ansible_access: Access,
repositories: list[Repository] | tuple[Repository, ...],
work_dir: Path | None = None,
):
self._ansible_access = ansible_access
self._repositories = tuple(repositories)
self._work_dir = work_dir
self._stack: ExitStack | None = None
def __enter__(self) -> Runner:
stack = ExitStack()
self._stack = stack
work_dir = self._work_dir
if work_dir is None:
temp_dir = stack.enter_context(tempfile.TemporaryDirectory())
work_dir = Path(temp_dir)
copier = AssetCopier(work_dir)
for repository in self._repositories:
for asset in repository.get_assets():
copier.copy(asset)
return Runner(self._ansible_access, work_dir)
def __exit__(self, exc_type, exc_value, traceback) -> None:
if self._stack is not None:
self._stack.close()
self._stack = None