Skip to content

Commit 2ef168c

Browse files
committed
Replace hardcoded configurations by play vars
1 parent 5bee22a commit 2ef168c

File tree

1 file changed

+40
-51
lines changed

1 file changed

+40
-51
lines changed

nspawn-runner

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import sys
1212
import shlex
1313
import shutil
1414

15+
log = logging.getLogger("nspawn-runner")
16+
1517
# Import YAML for parsing only. Prefer pyyaml for loading, because it's
1618
# significantly faster
1719
try:
@@ -31,33 +33,16 @@ except ModuleNotFoundError:
3133
def yaml_load(file):
3234
raise NotImplementedError("this feature requires PyYaml or ruamel.yaml")
3335

34-
CONFIG_DIR = "/etc/nspawn-runner"
36+
def systemd_version():
37+
res = subprocess.run(["systemd", "--version"], check=True, capture_output=True, text=True)
38+
return int(res.stdout.splitlines()[0].split()[1])
3539

36-
# Set to True to enable seccomp filtering when running CI jobs. This makes the
37-
# build slower, but makes sandboxing features available. See "Sandboxing" in
38-
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html
39-
ENABLE_SECCOMP = False
40+
CONFIG_DIR = "/etc/nspawn-runner"
41+
DATA_DIR = "/var/lib/nspawn-runner"
4042

43+
SYSTEMD_VERSION = systemd_version()
4144
EATMYDATA = shutil.which("eatmydata")
4245

43-
# See https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html
44-
45-
# If not None, set --property=CPUAccounting=yes and
46-
# --property=CPUWeight={CPU_WEIGHT} when starting systemd-nspawn
47-
CPU_WEIGHT = 50
48-
49-
# If not None, set --property=MemoryAccounting=yes and --property=MemoryHigh={MEMORY_HIGH}
50-
MEMORY_HIGH = "30%"
51-
# If not None, and MEMORY_HIGH is set, also set --property=MemoryMax={MEMORY_MAX}
52-
MEMORY_MAX = "40%"
53-
54-
# Set to true to use a tempfs overlay for writable storage. This makes CIs much
55-
# faster, if the machine configuration in terms of ram and swapspace has enough
56-
# capacity to handle disk space used for builds
57-
RAMDISK = False
58-
59-
log = logging.getLogger("nspawn-runner")
60-
6146

6247
def run_cmd(cmd: List[str], **kw) -> subprocess.CompletedProcess:
6348
"""
@@ -98,15 +83,13 @@ class NspawnRunner:
9883
self.root_dir = root_dir
9984
self.gitlab_build_dir = os.path.join(self.root_dir, ".build")
10085
self.gitlab_cache_dir = os.path.join(self.root_dir, ".cache")
101-
res = subprocess.run(["systemd", "--version"], check=True, capture_output=True, text=True)
102-
self.systemd_version = int(res.stdout.splitlines()[0].split()[1])
10386

10487
@classmethod
105-
def create(cls, root_dir: str):
88+
def create(cls, root_dir: str, ram_disk: bool):
10689
"""
10790
Instantiate the right NspawnRunner subclass for this sytem
10891
"""
109-
if RAMDISK:
92+
if ram_disk:
11093
return TmpfsRunner(root_dir)
11194

11295
# Detect filesystem type
@@ -181,7 +164,7 @@ class Machine:
181164
self.run_id = run_id
182165
self.machine_name = f"run-{self.run_id}"
183166

184-
def _run_nspawn(self, cmd: List[str]):
167+
def _run_nspawn(self, chroot: "Chroot", cmd: List[str]):
185168
"""
186169
Run the given systemd-nspawn command line, contained into its own unit
187170
using systemd-run
@@ -200,23 +183,29 @@ class Machine:
200183
'WatchdogSec=3min',
201184
]
202185

203-
if CPU_WEIGHT is not None:
186+
if chroot.config.get('cpu_weight') is not None:
204187
unit_config.append("CPUAccounting=yes")
205-
unit_config.append(f"CPUWeight={CPU_WEIGHT}")
188+
unit_config.append(f"CPUWeight={chroot.config.get('cpu_weight')}")
189+
190+
if chroot.config.get('memory_high') is not None:
191+
if not "MemoryAccounting=yes" in unit_config:
192+
unit_config.append("MemoryAccounting=yes")
193+
unit_config.append(f"MemoryHigh={chroot.config.get('memory_high')}")
206194

207-
if MEMORY_HIGH is not None:
208-
unit_config.append("MemoryAccounting=yes")
209-
unit_config.append(f"MemoryHigh={MEMORY_HIGH}")
210-
if MEMORY_MAX is not None and self.nspawn_runner.systemd_version >= 249:
211-
unit_config.append(f"MemoryMax={MEMORY_MAX}")
195+
if chroot.config.get('memory_max') is not None and SYSTEMD_VERSION >= 249:
196+
if not "MemoryAccounting=yes" in unit_config:
197+
unit_config.append("MemoryAccounting=yes")
198+
unit_config.append(f"MemoryMax={chroot.config.get('memory_max')}")
212199

213200
systemd_run_cmd = ["systemd-run"]
214-
if not ENABLE_SECCOMP:
201+
if not chroot.config.get('seccomp'):
215202
systemd_run_cmd.append("--setenv=SYSTEMD_SECCOMP=0")
203+
216204
for c in unit_config:
217205
systemd_run_cmd.append(f"--property={c}")
218206

219207
systemd_run_cmd.extend(cmd)
208+
systemd_run_cmd.extend(chroot.config.get('args', []))
220209

221210
log.info("Running %s", " ".join(shlex.quote(c) for c in systemd_run_cmd))
222211
os.execvp(systemd_run_cmd[0], systemd_run_cmd)
@@ -231,7 +220,7 @@ class Machine:
231220
f"--machine={self.machine_name}",
232221
"--boot", "--notify-ready=yes"]
233222

234-
if self.nspawn_runner.systemd_version >= 250:
223+
if SYSTEMD_VERSION >= 250:
235224
res.append("--suppress-sync=yes")
236225
return res
237226

@@ -288,7 +277,7 @@ class OverlayMachine(Machine):
288277
if os.path.exists(self.overlay_dir):
289278
raise Fail(f"overlay directory {self.overlay_dir} already exists")
290279
os.makedirs(self.overlay_dir, exist_ok=True)
291-
self._run_nspawn(self._get_nspawn_command(chroot))
280+
self._run_nspawn(chroot, self._get_nspawn_command(chroot))
292281

293282
def terminate(self):
294283
try:
@@ -311,7 +300,7 @@ class BtrfsMachine(Machine):
311300

312301
def start(self, chroot: "Chroot"):
313302
log.info("Starting machine using image %s", chroot.image_name)
314-
self._run_nspawn(self._get_nspawn_command(chroot))
303+
self._run_nspawn(chroot, self._get_nspawn_command(chroot))
315304

316305
def terminate(self):
317306
res = subprocess.run(["machinectl", "terminate", self.machine_name])
@@ -335,7 +324,7 @@ class TmpfsMachine(Machine):
335324

336325
def start(self, chroot: "Chroot"):
337326
log.info("Starting machine using image %s", chroot.image_name)
338-
self._run_nspawn(self._get_nspawn_command(chroot))
327+
self._run_nspawn(chroot, self._get_nspawn_command(chroot))
339328

340329
def terminate(self):
341330
res = subprocess.run(["machinectl", "terminate", self.machine_name])
@@ -351,6 +340,7 @@ class Chroot:
351340
self.nspawn_runner = nspawn_runner
352341
self.image_name = image_name
353342
self.chroot_dir = os.path.join(nspawn_runner.root_dir, self.image_name)
343+
self.config = self.load_config()
354344

355345
def exists(self) -> bool:
356346
"""
@@ -394,7 +384,7 @@ class Chroot:
394384
Login is done with exec, so this function, when successful, never
395385
returns and destroys the calling process
396386
"""
397-
cmd = ["systemd-nspawn", "--directory", self.chroot_dir]
387+
cmd = ["systemd-nspawn", "--directory", self.chroot_dir] + self.config.get('args', [])
398388
log.info("Running %s", " ".join(shlex.quote(c) for c in cmd))
399389
os.execvp(cmd[0], cmd)
400390

@@ -441,11 +431,11 @@ class Chroot:
441431

442432
# Extract what we need from the variables
443433
res: Dict[str, Any] = {}
444-
for var in ("chroot_suite", "maint_recreate"):
445-
key = f"nspawn_runner_{var}"
446-
if key not in pb_vars:
434+
prefix = 'nspawn_runner_'
435+
for key in pb_vars.keys():
436+
if not key.startswith(prefix):
447437
continue
448-
res[var] = pb_vars.get(key)
438+
res[key[len(prefix):]] = pb_vars.get(key)
449439

450440
return res
451441

@@ -505,12 +495,11 @@ class Chroot:
505495
log.error("%s: chroot configuration not found", self.image_name)
506496
return
507497
log.info("%s: running maintenance", self.image_name)
508-
config = self.load_config()
509-
if config.get("maint_recreate") and self.exists():
498+
if self.config.get("maint_recreate") and self.exists():
510499
log.info("%s: removing chroot to recreate it during maintenance", self.image_name)
511500
self.remove()
512501
if not self.exists():
513-
suite = config.get("chroot_suite")
502+
suite = self.config.get("chroot_suite")
514503
if suite is None:
515504
log.error("%s: chroot_suite not found in playbook, and chroot does not exist", self.image_name)
516505
return
@@ -629,7 +618,7 @@ class Command:
629618
def __init__(self, args):
630619
self.args = args
631620
self.setup_logging()
632-
self.nspawn_runner = NspawnRunner.create("/var/lib/nspawn-runner")
621+
self.nspawn_runner = NspawnRunner.create(DATA_DIR, self.args.ram_disk)
633622

634623
def setup_logging(self):
635624
# Setup logging
@@ -742,8 +731,7 @@ class ChrootCreate(ChrootMixin, SetupMixin, Command):
742731
self.chroot.must_not_exist()
743732
suite = self.args.suite
744733
if suite is None:
745-
config = self.chroot.load_config()
746-
suite = config.get("chroot_suite")
734+
suite = self.chroot.config.get("chroot_suite")
747735
if suite is None:
748736
suite = self.FALLBACK_SUITE
749737
self.chroot.create(suite)
@@ -885,6 +873,7 @@ def main():
885873
parser = argparse.ArgumentParser(description="Manage systemd-nspawn machines for CI runs.")
886874
parser.add_argument("-v", "--verbose", action="store_true", help="verbose output")
887875
parser.add_argument("--debug", action="store_true", help="verbose output")
876+
parser.add_argument("--ram-disk", action = "store_true", help="use ram disks")
888877
subparsers = parser.add_subparsers(help="sub-command help", dest="command")
889878

890879
ChrootList.make_subparser(subparsers)

0 commit comments

Comments
 (0)