Skip to content

Commit

Permalink
Added a Session object to hold shared resources during a work session…
Browse files Browse the repository at this point in the history
…. refs: #52
  • Loading branch information
spanezz committed Aug 15, 2022
1 parent c8a4d74 commit 4777f96
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 59 deletions.
23 changes: 15 additions & 8 deletions moncic/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ def make_subparser(cls, subparsers):
return parser

def run(self):
with self.moncic.images() as images:
with self.moncic.session() as session:
images = session.images()
with images.system(self.args.system) as system:
with checkout(system, self.args.repo, branch=self.args.branch) as srcdir:
if self.args.build_style:
Expand Down Expand Up @@ -181,7 +182,8 @@ def get_run_config(self) -> RunConfig:

@contextlib.contextmanager
def container(self):
with self.moncic.images() as images:
with self.moncic.session() as session:
images = session.images()
if self.args.maintenance:
make_system = images.maintenance_system
else:
Expand Down Expand Up @@ -290,7 +292,9 @@ def make_subparser(cls, subparsers):
return parser

def run(self):
with self.moncic.images() as images:
with self.moncic.session() as session:
images = session.images()

if not self.args.systems:
systems = images.list_images()
else:
Expand Down Expand Up @@ -329,7 +333,8 @@ def make_subparser(cls, subparsers):
return parser

def run(self):
with self.moncic.images() as images:
with self.moncic.session() as session:
images = session.images()
if not self.args.systems:
systems = images.list_images()
else:
Expand Down Expand Up @@ -370,7 +375,8 @@ def make_subparser(cls, subparsers):
return parser

def run(self):
with self.moncic.images() as images:
with self.moncic.session() as session:
images = session.images()
for name in self.args.systems:
images.remove_system(name)

Expand Down Expand Up @@ -435,7 +441,8 @@ def run(self):
TextColumn("Boostrapped"),
TextColumn("Path"))

with self.moncic.images() as images:
with self.moncic.session() as session:
images = session.images()
for name in images.list_images():
with images.system(name) as system:
output.add_row((name, system.distro.name, "yes" if system.is_bootstrapped() else "no", system.path))
Expand Down Expand Up @@ -473,5 +480,5 @@ class Dedup(MoncicCommand):
Deduplicate disk usage in image directories
"""
def run(self):
with self.moncic.images() as images:
images.deduplicate()
with self.moncic.session() as session:
session.images().deduplicate()
2 changes: 1 addition & 1 deletion moncic/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def get_start_command(self):
cmd.append("--read-only")
else:
cmd.append("--ephemeral")
if self.system.images.moncic.systemd_version >= 250:
if self.system.images.session.moncic.systemd_version >= 250:
cmd.append("--suppress-sync=yes")
return cmd

Expand Down
2 changes: 1 addition & 1 deletion moncic/distro.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ def __init__(self, name: str, suite: str, mirror: str = "http://deb.debian.org/d

def container_config_hook(self, system: System, config: ContainerConfig):
super().container_config_hook(system, config)
if system.images.moncic.config.debcachedir is not None:
if system.images.session.moncic.config.debcachedir is not None:
config.share_apt_cache = True

def get_base_packages(self) -> List[str]:
Expand Down
41 changes: 21 additions & 20 deletions moncic/imagestorage.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

if TYPE_CHECKING:
from .moncic import Moncic
from .session import Session

log = logging.getLogger("images")

Expand All @@ -27,8 +28,8 @@ class Images:
"""
Image storage made available as a directory in the file system
"""
def __init__(self, moncic: Moncic, imagedir: str):
self.moncic = moncic
def __init__(self, session: Session, imagedir: str):
self.session = session
self.imagedir = imagedir

def list_images(self) -> List[str]:
Expand Down Expand Up @@ -108,7 +109,7 @@ def add_dependencies(self, images: List[str]) -> List[str]:
from .system import SystemConfig
res: graphlib.TopologicalSorter = graphlib.TopologicalSorter()
for name in images:
config = SystemConfig.load(self.moncic.config, self.imagedir, name)
config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
if config.extends is not None:
res.add(config.name, config.extends)
else:
Expand All @@ -126,22 +127,22 @@ class PlainImages(Images):
"""
@contextlib.contextmanager
def system(self, name: str) -> Generator[System, None, None]:
system_config = SystemConfig.load(self.moncic.config, self.imagedir, name)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
# Force using tmpfs backing for ephemeral containers, since we cannot
# use snapshots
system_config.tmpfs = True
yield System(self, system_config)

@contextlib.contextmanager
def maintenance_system(self, name: str) -> Generator[MaintenanceSystem, None, None]:
system_config = SystemConfig.load(self.moncic.config, self.imagedir, name)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
# Force using tmpfs backing for ephemeral containers, since we cannot
# use snapshots
system_config.tmpfs = True
yield MaintenanceSystem(self, system_config)

def bootstrap_system(self, name: str):
system_config = SystemConfig.load(self.moncic.config, self.imagedir, name)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
if os.path.exists(system_config.path):
return

Expand Down Expand Up @@ -185,12 +186,12 @@ class BtrfsImages(Images):
"""
@contextlib.contextmanager
def system(self, name: str) -> Generator[System, None, None]:
system_config = SystemConfig.load(self.moncic.config, self.imagedir, name)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
yield System(self, system_config)

@contextlib.contextmanager
def maintenance_system(self, name: str) -> Generator[MaintenanceSystem, None, None]:
system_config = SystemConfig.load(self.moncic.config, self.imagedir, name)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
path = os.path.join(self.imagedir, name)
work_path = path + ".new"
if os.path.exists(work_path):
Expand All @@ -209,7 +210,7 @@ def maintenance_system(self, name: str) -> Generator[MaintenanceSystem, None, No
os.rename(work_path, path)
else:
# Update
subvolume = Subvolume(system_config, self.moncic.config)
subvolume = Subvolume(system_config, self.session.moncic.config)
# Create work_path as a snapshot of path
subvolume.snapshot(path)
try:
Expand All @@ -224,7 +225,7 @@ def maintenance_system(self, name: str) -> Generator[MaintenanceSystem, None, No
subvolume.replace_subvolume(path)

def bootstrap_system(self, name: str):
system_config = SystemConfig.load(self.moncic.config, self.imagedir, name)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, name)
if os.path.exists(system_config.path):
return

Expand All @@ -237,11 +238,11 @@ def bootstrap_system(self, name: str):
try:
if system_config.extends is not None:
with self.system(system_config.extends) as parent:
subvolume = Subvolume(system_config, self.moncic.config)
subvolume = Subvolume(system_config, self.session.moncic.config)
subvolume.snapshot(parent.path)
else:
tarball_path = self.get_distro_tarball(system_config.distro)
subvolume = Subvolume(system_config, self.moncic.config)
subvolume = Subvolume(system_config, self.session.moncic.config)
with subvolume.create():
if tarball_path is not None:
# Shortcut in case we have a chroot in a tarball
Expand All @@ -262,8 +263,8 @@ def remove_system(self, name: str):
path = os.path.join(self.imagedir, name)
if not os.path.exists(path):
return
system_config = SystemConfig.load(self.moncic.config, self.imagedir, path)
subvolume = Subvolume(system_config, self.moncic.config)
system_config = SystemConfig.load(self.session.moncic.config, self.imagedir, path)
subvolume = Subvolume(system_config, self.session.moncic.config)
subvolume.remove()

def deduplicate(self):
Expand Down Expand Up @@ -315,8 +316,8 @@ class ImageStorage:
"""
Interface for handling image storage
"""
def __init__(self, moncic: Moncic):
self.moncic = moncic
def __init__(self, session: Session):
self.session = session

@contextlib.contextmanager
def images(self) -> Generator[Images, None, None]:
Expand Down Expand Up @@ -362,7 +363,7 @@ def __init__(self, moncic: Moncic, imagedir: str):

@contextlib.contextmanager
def images(self) -> Generator[Images, None, None]:
yield PlainImages(self.moncic, self.imagedir)
yield PlainImages(self.session, self.imagedir)


class BtrfsImageStorage(ImageStorage):
Expand All @@ -375,7 +376,7 @@ def __init__(self, moncic: Moncic, imagedir: str):

@contextlib.contextmanager
def images(self) -> Generator[Images, None, None]:
yield BtrfsImages(self.moncic, self.imagedir)
yield BtrfsImages(self.session, self.imagedir)


class PlainMachineImageStorage(PlainImageStorage):
Expand All @@ -388,7 +389,7 @@ def __init__(self, moncic: Moncic):

@contextlib.contextmanager
def images(self) -> Generator[Images, None, None]:
yield Images(self.moncic, self.imagedir)
yield Images(self.session, self.imagedir)


class BtrfsMachineImageStorage(BtrfsImageStorage):
Expand All @@ -401,4 +402,4 @@ def __init__(self, moncic: Moncic):

@contextlib.contextmanager
def images(self) -> Generator[Images, None, None]:
yield BtrfsImages(self.moncic, self.imagedir)
yield BtrfsImages(self.session, self.imagedir)
19 changes: 3 additions & 16 deletions moncic/moncic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import yaml

from . import imagestorage
from .session import Session
from .privs import ProcessPrivs

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -137,22 +137,9 @@ def __init__(
self.config = config
self.privs.auto_sudo = self.config.auto_sudo

# Storage for OS images
self.image_storage: imagestorage.ImageStorage
if self.config.imagedir is None:
self.image_storage = imagestorage.ImageStorage.create_default(self)
else:
self.image_storage = imagestorage.ImageStorage.create(self, self.config.imagedir)

# Detect systemd's version
res = subprocess.run(["systemctl", "--version"], check=True, capture_output=True, text=True)
self.systemd_version = int(res.stdout.splitlines()[0].split()[1])

def images(self) -> ContextManager[imagestorage.Images]:
return self.image_storage.images()

def set_imagedir(self, imagedir: str):
"""
Set the image directory, overriding the one from config
"""
self.image_storage = imagestorage.ImageStorage.create(self, imagedir)
def session(self) -> ContextManager[Session]:
return Session(self)
40 changes: 40 additions & 0 deletions moncic/session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from __future__ import annotations

import contextlib
from typing import Optional, TYPE_CHECKING

from . import imagestorage

if TYPE_CHECKING:
from .moncic import Moncic


class Session(contextlib.ExitStack):
"""
Hold shared resourcse during a single-threaded Moncic-CI work session
"""
def __init__(self, moncic: Moncic):
super().__init__()
self.moncic = moncic

# Storage for OS images
self.image_storage: imagestorage.ImageStorage
if self.moncic.config.imagedir is None:
self.image_storage = imagestorage.ImageStorage.create_default(self)
else:
self.image_storage = imagestorage.ImageStorage.create(self, self.moncic.config.imagedir)

# Images contained in the image storage
self._images: Optional[imagestorage.Images] = None

def images(self) -> imagestorage.Images:
"""
Return the Images storage
"""
if self._images is None:
self._images = self.enter_context(self.image_storage.images())
return self._images

# def __enter__(self):
# super().__enter__()
# return self
4 changes: 2 additions & 2 deletions moncic/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,14 @@ def container_config(self, config: Optional[ContainerConfig] = None) -> Containe
if self.config.tmpfs is not None:
config.tmpfs = self.config.tmpfs
else:
config.tmpfs = self.images.moncic.config.tmpfs
config.tmpfs = self.images.session.moncic.config.tmpfs
elif config.ephemeral and config.tmpfs is None:
# Make a copy to prevent changing the caller's config
config = dataclasses.replace(config)
if self.config.tmpfs is not None:
config.tmpfs = self.config.tmpfs
else:
config.tmpfs = self.images.moncic.config.tmpfs
config.tmpfs = self.images.session.moncic.config.tmpfs

# Allow distro-specific setup
self.distro.container_config_hook(self, config)
Expand Down
4 changes: 2 additions & 2 deletions moncic/unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,8 @@ def _load(mconfig: MoncicConfig, imagedir: str, name: str):
return SystemConfig(name=name, path=os.path.join(mconfig.imagedir, "test"), distro=distro.name)

with mock.patch("moncic.system.SystemConfig.load", new=_load):
with moncic.images() as images:
yield images
with moncic.session() as session:
yield session.images()

@contextlib.contextmanager
def make_system(self, distro: Distro) -> Generator[MaintenanceSystem, None, None]:
Expand Down
15 changes: 10 additions & 5 deletions tests/test_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def test_tarball(self):

with self.mock() as run_log:
moncic = make_moncic(mconfig)
with moncic.images() as images:
with moncic.session() as session:
images = session.images()
images.bootstrap_system("test")
with images.system("test") as system:
path = system.path
Expand All @@ -40,7 +41,8 @@ def test_forward_user(self):

with self.mock() as run_log:
moncic = make_moncic(mconfig)
with moncic.images() as images:
with moncic.session() as session:
images = session.images()
with images.maintenance_system("test") as system:
system.update()

Expand All @@ -60,7 +62,8 @@ def test_snapshot_bootstrap(self):

with self.mock() as run_log:
moncic = make_moncic(mconfig)
with moncic.images() as images:
with moncic.session() as session:
images = session.images()
images.bootstrap_system("test")
with images.system("test") as system:
path = system.path
Expand Down Expand Up @@ -88,7 +91,8 @@ def test_snapshot_update(self):

with self.mock() as run_log:
moncic = make_moncic(mconfig)
with moncic.images() as images:
with moncic.session() as session:
images = session.images()
with images.maintenance_system("test") as system:
system.update()
path = system.path[:-4]
Expand All @@ -111,7 +115,8 @@ def test_compression(self):

with self.mock() as run_log:
moncic = make_moncic(mconfig)
with moncic.images() as images:
with moncic.session() as session:
images = session.images()
images.bootstrap_system("test")
with images.system("test") as system:
path = system.path
Expand Down
Loading

0 comments on commit 4777f96

Please sign in to comment.