osbuild: split package into separate files
Import modules between files using the syntax `from . import foobar`, renaming what used to be `FooBar` to `foobar.FooBar` when moved to a separate file. In __init__.py only import what is meant to be public API. Signed-off-by: Tom Gundersen <teg@jklm.no>
This commit is contained in:
parent
f54fbe2912
commit
679b79c5e5
7 changed files with 515 additions and 480 deletions
87
osbuild/objectstore.py
Normal file
87
osbuild/objectstore.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
import contextlib
|
||||
import errno
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from . import treesum
|
||||
|
||||
|
||||
__all__ = [
|
||||
"ObjectStore",
|
||||
]
|
||||
|
||||
|
||||
class ObjectStore:
|
||||
def __init__(self, store):
|
||||
self.store = store
|
||||
self.objects = f"{store}/objects"
|
||||
self.refs = f"{store}/refs"
|
||||
os.makedirs(self.store, exist_ok=True)
|
||||
os.makedirs(self.objects, exist_ok=True)
|
||||
os.makedirs(self.refs, exist_ok=True)
|
||||
|
||||
def has_tree(self, tree_id):
|
||||
if not tree_id:
|
||||
return False
|
||||
return os.access(f"{self.refs}/{tree_id}", os.F_OK)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def get_tree(self, tree_id):
|
||||
with tempfile.TemporaryDirectory(dir=self.store) as tmp:
|
||||
if tree_id:
|
||||
subprocess.run(["mount", "-o", "bind,ro,mode=0755", f"{self.refs}/{tree_id}", tmp], check=True)
|
||||
try:
|
||||
yield tmp
|
||||
finally:
|
||||
subprocess.run(["umount", "--lazy", tmp], check=True)
|
||||
else:
|
||||
# None was given as tree_id, just return an empty directory
|
||||
yield tmp
|
||||
|
||||
@contextlib.contextmanager
|
||||
def new_tree(self, tree_id, base_id=None):
|
||||
with tempfile.TemporaryDirectory(dir=self.store) as tmp:
|
||||
# the tree that is yielded will be added to the content store
|
||||
# on success as tree_id
|
||||
|
||||
tree = f"{tmp}/tree"
|
||||
link = f"{tmp}/link"
|
||||
os.mkdir(tree, mode=0o755)
|
||||
|
||||
if base_id:
|
||||
# the base, the working tree and the output tree are all on
|
||||
# the same fs, so attempt a lightweight copy if the fs
|
||||
# supports it
|
||||
subprocess.run(["cp", "--reflink=auto", "-a", f"{self.refs}/{base_id}/.", tree], check=True)
|
||||
|
||||
yield tree
|
||||
|
||||
# if the yield raises an exception, the working tree is cleaned
|
||||
# up by tempfile, otherwise, we save it in the correct place:
|
||||
fd = os.open(tree, os.O_DIRECTORY)
|
||||
try:
|
||||
m = hashlib.sha256()
|
||||
treesum.treesum(m, fd)
|
||||
treesum_hash = m.hexdigest()
|
||||
finally:
|
||||
os.close(fd)
|
||||
# the tree is stored in the objects directory using its content
|
||||
# hash as its name, ideally a given tree_id (i.e., given config)
|
||||
# will always produce the same content hash, but that is not
|
||||
# guaranteed
|
||||
output_tree = f"{self.objects}/{treesum_hash}"
|
||||
try:
|
||||
os.rename(tree, output_tree)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOTEMPTY:
|
||||
pass # tree with the same content hash already exist, use that
|
||||
else:
|
||||
raise
|
||||
# symlink the tree_id (config hash) in the refs directory to the treesum
|
||||
# (content hash) in the objects directory. If a symlink by that name
|
||||
# alreday exists, atomically replace it, but leave the backing object
|
||||
# in place (it may be in use).
|
||||
os.symlink(f"../objects/{treesum_hash}", link)
|
||||
os.replace(link, f"{self.refs}/{tree_id}")
|
||||
Loading…
Add table
Add a link
Reference in a new issue