]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
serve: Add StorageTargetMode= feature
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 10 Feb 2025 18:18:46 +0000 (19:18 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 11 Feb 2025 12:50:23 +0000 (13:50 +0100)
Let's make it configurable whether we run systemd-storagetm as part
of serve.

mkosi/__init__.py
mkosi/config.py
mkosi/resources/man/mkosi.1.md
tests/test_json.py

index d710d29c88f90aba219e66f4d5d4721c18f84151..9d8698c1cb1086503af43ceca11f8fa37dc23902 100644 (file)
@@ -101,6 +101,7 @@ from mkosi.qemu import (
     start_journal_remote,
 )
 from mkosi.run import (
+    Popen,
     apivfs_options,
     chroot_cmd,
     chroot_options,
@@ -4310,14 +4311,42 @@ def run_coredumpctl(args: Args, config: Config) -> None:
     run_systemd_tool("coredumpctl", args, config)
 
 
+def start_storage_target_mode(config: Config) -> AbstractContextManager[Optional[Popen]]:
+    if config.storage_target_mode == ConfigFeature.disabled:
+        return contextlib.nullcontext()
+
+    if config.storage_target_mode == ConfigFeature.auto and os.getuid() != 0:
+        return contextlib.nullcontext()
+
+    if config.output_format != OutputFormat.disk:
+        if config.storage_target_mode == ConfigFeature.enabled:
+            die("Storage target mode is only supported for the 'disk' output format")
+
+        return contextlib.nullcontext()
+
+    if not config.find_binary("/usr/lib/systemd/systemd-storagetm"):
+        if config.storage_target_mode == ConfigFeature.enabled:
+            die("Storage target mode enabled but systemd-storagetm is not installed")
+
+        return contextlib.nullcontext()
+
+    return spawn(
+        ["/usr/lib/systemd/systemd-storagetm", config.output_with_format],
+        stdin=sys.stdin,
+        stdout=sys.stdout,
+        sandbox=config.sandbox(
+            network=True,
+            relaxed=True,
+            options=["--chdir", config.output_dir_or_cwd()],
+            setup=become_root_cmd(),
+        ),
+    )
+
+
 def run_serve(args: Args, config: Config) -> None:
     """Serve the output directory via a tiny HTTP server"""
 
     with contextlib.ExitStack() as stack:
-        want_storagetm = config.output_format == OutputFormat.disk and config.find_binary(
-            "/usr/lib/systemd/systemd-storagetm"
-        )
-
         http = stack.enter_context(
             spawn(
                 [python_binary(config), "-m", "http.server", "8081"],
@@ -4331,26 +4360,15 @@ def run_serve(args: Args, config: Config) -> None:
             )
         )
 
-        if want_storagetm:
-            storagetm = stack.enter_context(
-                spawn(
-                    ["/usr/lib/systemd/systemd-storagetm", config.output_with_format],
-                    stdin=sys.stdin,
-                    stdout=sys.stdout,
-                    sandbox=config.sandbox(
-                        network=True,
-                        relaxed=True,
-                        options=["--chdir", config.output_dir_or_cwd()],
-                        setup=become_root_cmd(),
-                    ),
-                )
-            )
+        storagetm = stack.enter_context(start_storage_target_mode(config))
 
+        # If we run systemd-storagetm with run0, it replaces the foreground process group with its own which
+        # means the http process doesn't get SIGINT from the terminal, so let's send it ourselves in that
+        # case.
+        if storagetm and os.getuid() != 0:
             storagetm.wait()
             http.send_signal(signal.SIGINT)
 
-        http.wait()
-
 
 def generate_key_cert_pair(args: Args) -> None:
     """Generate a private key and accompanying X509 certificate using openssl"""
index ed24c7d15525d06c316b869e8eb65a9a5a7790ad..c18f95c8cfdcff1f1496b9e5f645eceb502cf7af 100644 (file)
@@ -1943,6 +1943,7 @@ class Config:
     credentials: dict[str, str]
     kernel_command_line_extra: list[str]
     register: ConfigFeature
+    storage_target_mode: ConfigFeature
     runtime_trees: list[ConfigTree]
     runtime_size: Optional[int]
     runtime_scratch: ConfigFeature
@@ -3825,6 +3826,14 @@ SETTINGS: list[ConfigSetting[Any]] = [
         default=ConfigFeature.auto,
         help="Register booted vm/container with systemd-machined",
     ),
+    ConfigSetting(
+        dest="storage_target_mode",
+        metavar="FEATURE",
+        section="Runtime",
+        parse=config_parse_feature,
+        default=ConfigFeature.auto,
+        help="Run systemd-storagetm as part of the serve verb",
+    ),
 ]
 SETTINGS_LOOKUP_BY_NAME = {name: s for s in SETTINGS for name in [s.name, *s.compat_names]}
 SETTINGS_LOOKUP_BY_DEST = {s.dest: s for s in SETTINGS}
@@ -5061,6 +5070,7 @@ def summary(config: Config) -> str:
                             Machine: {config.machine_or_name()}
                     Forward Journal: {none_to_none(config.forward_journal)}
        Register guest with machined: {config.register}
+                Storage Target Mode: {config.storage_target_mode}
 
             Virtual Machine Monitor: {config.vmm}
                             Console: {config.console}
index a8d6cb9c4c779519725f570281a5f4e1d3d12340..e402e38dd9dd4ab846945ef0ef8bce5337cc3e0a 100644 (file)
@@ -1850,6 +1850,16 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     of file if your workload produces more than `4G` worth of journal
     data.
 
+`StorageTargetMode=`, `--storage-target-mode=`
+:   Specifies whether the `serve` verb should start
+    **systemd-storagetm** to serve disk images over NVME-TCP. Takes a
+    boolean value or `auto`. If enabled, systemd-storagetm is always
+    started and mkosi will fail if it cannot start systemd-storagetm. If
+    disabled, systemd-storagetm is never started. If `auto`,
+    systemd-storagetm will be started if a disk image is being built,
+    the systemd-storagetm binary is found and `mkosi serve` is being
+    invoked as the root user.
+
 `SysupdateDirectory=`, `--sysupdate-directory=`
 :   Path to a directory containing systemd-sysupdate transfer definition
     files that are used by `mkosi sysupdate`. If `mkosi.sysupdate/`
index 31265dfc13e097d1f25f7e21a84f3e2e572ddac7..a5be927bcb4b9561cbe68fd70c087beec6a380c4 100644 (file)
@@ -353,6 +353,7 @@ def test_config() -> None:
             "Ssh": false,
             "SshCertificate": "/path/to/cert",
             "SshKey": null,
+            "StorageTargetMode": "enabled",
             "SyncScripts": [
                 "/sync"
             ],
@@ -566,6 +567,7 @@ def test_config() -> None:
         ssh_certificate=Path("/path/to/cert"),
         ssh_key=None,
         ssh=False,
+        storage_target_mode=ConfigFeature.enabled,
         sync_scripts=[Path("/sync")],
         sysupdate_dir=Path("/sysupdate"),
         timezone=None,