stages: add org.osbuild.ostree.fillvar
Pre-populate /var directory for a given deployment.
This commit is contained in:
parent
d284bc0ef2
commit
98d0a856df
1 changed files with 124 additions and 0 deletions
124
stages/org.osbuild.ostree.fillvar
Executable file
124
stages/org.osbuild.ostree.fillvar
Executable file
|
|
@ -0,0 +1,124 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
"""
|
||||||
|
Pre-populate /var directory for a given stateroot.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import osbuild.api
|
||||||
|
from osbuild.util import ostree
|
||||||
|
|
||||||
|
|
||||||
|
SCHEMA = """
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["deployment"],
|
||||||
|
"properties": {
|
||||||
|
"deployment": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["osname", "ref"],
|
||||||
|
"properties": {
|
||||||
|
"osname": {
|
||||||
|
"description": "Name of the stateroot to be used in the deployment",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ref": {
|
||||||
|
"description": "OStree ref to create and use for deployment",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"serial": {
|
||||||
|
"description": "The deployment serial (usually '0')",
|
||||||
|
"type": "number",
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MountGuard(contextlib.AbstractContextManager):
|
||||||
|
def __init__(self):
|
||||||
|
self.mounts = []
|
||||||
|
|
||||||
|
def mount(self, source, target, bind=True, ro=False, mode="0755"):
|
||||||
|
options = []
|
||||||
|
if bind:
|
||||||
|
options += ["bind"]
|
||||||
|
if ro:
|
||||||
|
options += ["ro"]
|
||||||
|
if mode:
|
||||||
|
options += [mode]
|
||||||
|
|
||||||
|
args = ["--make-private"]
|
||||||
|
if options:
|
||||||
|
args += ["-o", ",".join(options)]
|
||||||
|
|
||||||
|
subprocess.run(["mount"] + args + [source, target], check=True)
|
||||||
|
self.mounts += [{"source": source, "target": target}]
|
||||||
|
|
||||||
|
subprocess.run(["mount"] + args + [source, target], check=True)
|
||||||
|
|
||||||
|
def unmount(self):
|
||||||
|
|
||||||
|
while self.mounts:
|
||||||
|
mount = self.mounts.pop() # FILO: get the last mount
|
||||||
|
target = mount["target"]
|
||||||
|
subprocess.run(["umount", "--lazy", target],
|
||||||
|
check=True)
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.unmount()
|
||||||
|
|
||||||
|
|
||||||
|
def populate_var(sysroot):
|
||||||
|
# Like anaconda[1] and Fedora CoreOS dracut[2]
|
||||||
|
# [1] pyanaconda/payload/rpmostreepayload.py
|
||||||
|
# [2] ignition-ostree-populate-var.sh
|
||||||
|
|
||||||
|
for target in ('lib', 'log'):
|
||||||
|
os.makedirs(f"{sysroot}/var/{target}", exist_ok=True)
|
||||||
|
|
||||||
|
for target in ('home', 'roothome', 'lib/rpm', 'opt', 'srv',
|
||||||
|
'usrlocal', 'mnt', 'media', 'spool', 'spool/mail'):
|
||||||
|
|
||||||
|
if os.path.exists(f"{sysroot}/var/{target}"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
res = subprocess.run(["systemd-tmpfiles", "--create", "--boot",
|
||||||
|
"--root=" + sysroot,
|
||||||
|
"--prefix=/var/" + target],
|
||||||
|
encoding="utf-8",
|
||||||
|
stdout=sys.stderr,
|
||||||
|
check=False)
|
||||||
|
|
||||||
|
# According to systemd-tmpfiles(8), the return values are:
|
||||||
|
# 0 → success
|
||||||
|
# 65 → so some lines had to be ignored, but no other errors
|
||||||
|
# 73 → configuration ok, but could not be created
|
||||||
|
# 1 → other error
|
||||||
|
if res.returncode not in [0, 65]:
|
||||||
|
raise RuntimeError(f"Failed to provision /var/{target}")
|
||||||
|
|
||||||
|
|
||||||
|
def main(tree, options):
|
||||||
|
dep = options["deployment"]
|
||||||
|
osname = dep["osname"]
|
||||||
|
ref = dep["ref"]
|
||||||
|
serial = dep.get("serial", 0)
|
||||||
|
|
||||||
|
deployment = ostree.deployment_path(tree, osname, ref, serial)
|
||||||
|
|
||||||
|
with MountGuard() as mgr:
|
||||||
|
mgr.mount(f"{deployment}/var", f"{deployment}/var")
|
||||||
|
populate_var(deployment)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
stage_args = osbuild.api.arguments()
|
||||||
|
r = main(stage_args["tree"],
|
||||||
|
stage_args["options"])
|
||||||
|
sys.exit(r)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue