Repository: gabrieldemarmiesse/python-on-whales (language: python)

Issue:
Support podman's "systemd" option when creating a container
Podman supports a `--systemd={true,false,always}` option to `podman run` or `podman create` ([docs](https://docs.podman.io/en/latest/markdown/podman-create.1.html#systemd-true-false-always)). There is no expectation that Docker will implement this (Docker actively doesn't want to support running systemd in a container), so I'm not sure how you feel about adding this option?

Unfortunately there's a Podman [bug](https://github.com/containers/podman/issues/15647) where when it tries to run a container in systemd mode it fails on non-systemd hosts using cgroups v1. The default behaviour is `--systemd=true`, which really means 'detect', i.e. run in systemd mode if the container is detected to use systemd. This means that with podman v3 a workaround is required to be able to start a systemd container on such hosts (such as Alpine 3.15).

Possible workarounds:
- Explicitly disable systemd mode (parity with Docker behaviour) with `--systemd=false`
- Manually create the `/sys/fs/cgroup/systemd/` mount on the host
- Use latest podman, use cgroups v2, or use docker...

My preferred, normal solution is the first - fundamentally this is podman adding special handling but in a broken way, so I just want to disable that special handling... Podman is much easier to get going with on Alpine, and I'm specifically wanting to test on a cgroups v1 host.

I appreciate due to this only being requested to allow working around an issue in podman and with the option not being provided by docker it might not be a very appealing thing to add to the API. However, if not all arguments will be supported it would be nice if there was at least an escape hatch to pass arbitrary arguments through to the container client rather than getting something like "TypeError: run() got an unexpected keyword argument 'systemd'".

What do you think?


Discussion / hints:
I am open to support options that are present in certain implementations of Docker and not others. Though I will limit it to 
* Docker
* Podman
* Nerdctl

Feel free to make a pull request, don't forget to document in the docstring that this option is only available in podman, and do not make a runtime check to disable the option in other clients than podman. I don't think it's possible to do a reliable runtime check because users can refer to aliases in  `client_call`, so it's actually impossible to know. Also because this option might be supported later by some clients and might be supported by clients we have no knowledge about
Ok fair enough, I'll hopefully get a chance to create a PR at some point, thanks.

Fix patch:
diff --git a/python_on_whales/components/container/cli_wrapper.py b/python_on_whales/components/container/cli_wrapper.py
index c86cd1eb..1320ef7c 100644
--- a/python_on_whales/components/container/cli_wrapper.py
+++ b/python_on_whales/components/container/cli_wrapper.py
@@ -607,6 +607,7 @@ def create(
         stop_timeout: Optional[int] = None,
         storage_options: List[str] = [],
         sysctl: Dict[str, str] = {},
+        systemd: Optional[Union[bool, Literal["always"]]] = None,
         tmpfs: List[ValidPath] = [],
         ulimit: List[str] = [],
         user: Optional[str] = None,
@@ -769,6 +770,7 @@ def create(
 
         full_cmd.add_args_list("--storage-opt", storage_options)
         full_cmd.add_args_list("--sysctl", format_dict_for_cli(sysctl))
+        full_cmd.add_simple_arg("--systemd", systemd)
         full_cmd.add_args_list("--tmpfs", tmpfs)
         full_cmd.add_args_list("--ulimit", ulimit)
 
@@ -1318,6 +1320,7 @@ def run(
         storage_options: List[str] = [],
         stream: bool = False,
         sysctl: Dict[str, str] = {},
+        systemd: Optional[Union[bool, Literal["always"]]] = None,
         tmpfs: List[ValidPath] = [],
         tty: bool = False,
         ulimit: List[str] = [],
@@ -1474,6 +1477,8 @@ def run(
                 `"4g"` for example.
             stop_timeout: Signal to stop a container (default "SIGTERM")
             storage_options: Storage driver options for the container
+            systemd: Whether to run in systemd mode. Only known to apply to Podman, see
+                https://docs.podman.io/en/latest/markdown/podman-run.1.html#systemd-true-false-always
             tty: Allocate a pseudo-TTY. Allow the process to access your terminal
                 to write on it.
             user: Username or UID (format: `<name|uid>[:<group|gid>]`)
@@ -1641,6 +1646,7 @@ def run(
 
         full_cmd.add_args_list("--storage-opt", storage_options)
         full_cmd.add_args_list("--sysctl", format_dict_for_cli(sysctl))
+        full_cmd.add_simple_arg("--systemd", systemd)
         full_cmd.add_args_list("--tmpfs", tmpfs)
         full_cmd.add_args_list("--ulimit", ulimit)
 


Test patch:
diff --git a/tests/python_on_whales/components/test_container.py b/tests/python_on_whales/components/test_container.py
index cb6d3821..c9174b75 100644
--- a/tests/python_on_whales/components/test_container.py
+++ b/tests/python_on_whales/components/test_container.py
@@ -5,6 +5,7 @@
 import time
 from datetime import datetime, timedelta, timezone
 from pathlib import Path
+from typing import Literal, Union
 from unittest.mock import Mock, patch
 
 import pytest
@@ -157,6 +158,29 @@ def test_container_create_with_cgroupns():
         assert container.host_config.cgroupns_mode == "host"
 
 
+@pytest.mark.parametrize("systemd_mode", [True, False, "always"])
+@patch("python_on_whales.components.container.cli_wrapper.run")
+@patch("python_on_whales.components.container.cli_wrapper.Container", Mock())
+def test_container_create_with_systemd_mode(
+    run_mock: Mock, systemd_mode: Union[bool, Literal["always"]]
+):
+    docker.container.create(
+        "ubuntu", ["sleep", "infinity"], systemd=systemd_mode, pull="never"
+    )
+    run_mock.assert_called_once_with(
+        docker.client_config.docker_cmd
+        + [
+            # fmt: off
+            "create",
+            "--pull", "never",
+            "--systemd", systemd_mode,
+            "ubuntu",
+            "sleep", "infinity",
+            # fmt: on
+        ]
+    )
+
+
 def test_fails_correctly_create_start():
     python_code = """
 import sys