From 9e3020a9df1cb13d1ced99d664e614b51afd09c5 Mon Sep 17 00:00:00 2001
From: Muayyad alsadi <alsadi@gmail.com>
Date: Sat, 12 Mar 2022 23:08:24 +0300
Subject: [PATCH] FIXES #307: systemd command

---
 podman_compose.py | 86 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 78 insertions(+), 8 deletions(-)

diff --git a/podman_compose.py b/podman_compose.py
index 8806280..95a6f69 100755
--- a/podman_compose.py
+++ b/podman_compose.py
@@ -1316,7 +1316,8 @@ class PodmanCompose:
                 sys.exit(1)
             log("using podman version: " + self.podman_version)
         cmd_name = args.command
-        if cmd_name != "version":
+        compose_required = cmd_name != "version" and (cmd_name != "systemd" or args.action!="create-unit")
+        if compose_required:
             self._parse_compose_file()
         cmd = self.commands[cmd_name]
         cmd(self, args)
@@ -1324,6 +1325,9 @@ class PodmanCompose:
     def _parse_compose_file(self):
         args = self.global_args
         # cmd = args.command
+        dirname = os.environ.get("COMPOSE_PROJECT_DIR", None)
+        if dirname and os.path.isdir(dirname):
+            os.chdir(dirname)
         pathsep = os.environ.get("COMPOSE_PATH_SEPARATOR", None) or os.pathsep
         if not args.file:
             default_str = os.environ.get("COMPOSE_FILE", None)
@@ -1370,10 +1374,7 @@ class PodmanCompose:
         self.project_name = project_name
 
         dotenv_path = os.path.join(dirname, args.env_file)
-        print(dotenv_path)
-        self.environ = dict(os.environ)
         dotenv_dict = dotenv_to_dict(dotenv_path)
-        self.environ.update(dotenv_dict)
         os.environ.update(
             {
                 key: value
@@ -1381,11 +1382,14 @@ class PodmanCompose:
                 if key.startswith("PODMAN_")
             }
         )
+        self.environ = dict(os.environ)
+        self.environ.update(dotenv_dict)
         # see: https://docs.docker.com/compose/reference/envvars/
         # see: https://docs.docker.com/compose/env-file/
         self.environ.update(
             {
-                "COMPOSE_FILE": os.path.basename(filename),
+                "COMPOSE_PROJECT_DIR": dirname,
+                "COMPOSE_FILE": pathsep.join(relative_files),
                 "COMPOSE_PROJECT_NAME": self.project_name,
                 "COMPOSE_PATH_SEPARATOR": pathsep,
             }
@@ -1458,7 +1462,7 @@ class PodmanCompose:
         podman_compose_labels = [
             "io.podman.compose.config-hash=" + self.yaml_hash,
             "io.podman.compose.project=" + project_name,
-            "io.podman.compose.version=0.0.1",
+            "io.podman.compose.version={__version__}",
             "com.docker.compose.project=" + project_name,
             "com.docker.compose.project.working_dir=" + dirname,
             "com.docker.compose.project.config_files=" + ",".join(relative_files),
@@ -1626,7 +1630,7 @@ podman_compose = PodmanCompose()
 
 
 class cmd_run:  # pylint: disable=invalid-name,too-few-public-methods
-    def __init__(self, compose, cmd_name, cmd_desc):
+    def __init__(self, compose, cmd_name, cmd_desc=None):
         self.compose = compose
         self.cmd_name = cmd_name
         self.cmd_desc = cmd_desc
@@ -1636,7 +1640,7 @@ class cmd_run:  # pylint: disable=invalid-name,too-few-public-methods
             return func(*args, **kw)
 
         wrapped._compose = self.compose
-        wrapped.desc = self.cmd_desc
+        wrapped.desc = self.cmd_desc or func.__doc__
         wrapped._parse_args = []
         self.compose.commands[self.cmd_name] = wrapped
         return wrapped
@@ -1685,6 +1689,7 @@ def is_local(container: dict) -> bool:
         else container["image"].startswith("localhost/")
     )
 
+
 @cmd_run(podman_compose, "wait", "wait running containers to stop")
 def compose_wait(compose, args):
     containers = [cnt["name"] for cnt in compose.containers]
@@ -1692,6 +1697,62 @@ def compose_wait(compose, args):
     cmd_args.extend(containers)
     compose.podman.run([], "wait", cmd_args, sleep=0)
 
+
+@cmd_run(podman_compose, "systemd")
+def compose_systemd(compose, args):
+    """
+    create systemd unit file and register its compose stacks
+    
+    When first installed type `sudo podman-compose -a create-unit`
+    later you can add a compose stack by running `podman-compose -a register`
+    then you can start/stop your stack with `systemctl --user start podman-compose@<PROJ>`
+    """
+    stacks_dir = ".config/containers/compose/projects"
+    if args.action=="register":
+        proj_name = compose.project_name
+        fn = os.path.expanduser(f"~/{stacks_dir}/{proj_name}.env")
+        os.makedirs(os.path.dirname(fn), exist_ok=True)
+        print(f"writing [{fn}]: ...")
+        with open(fn, "w") as f: 
+            for k, v in compose.environ.items():
+                if k.startswith("COMPOSE_") or k.startswith("PODMAN_"):
+                    f.write(f"{k}={v}\n")
+        print(f"writing [{fn}]: done.")
+        print(f"""later you can use use enable, start, stop, status, cat
+like this `systemctl --user enable --now podman-compose@{proj_name}`""")
+    elif args.action=="create-unit":
+        script = os.path.realpath(sys.argv[0])
+        fn = "/usr/lib/systemd/user/podman-compose@.service"
+        out = f"""\
+# {fn}
+
+[Unit]
+Description=%i rootless pod (podman-compose)
+
+[Service]
+Type=simple
+EnvironmentFile=%h/{stacks_dir}/%i.env
+ExecStartPre={script} up --no-start
+ExecStartPre=/usr/bin/podman pod start pod_%i
+ExecStart={script} wait
+ExecStop=/usr/bin/podman pod stop pod_%i
+
+[Install]
+WantedBy=default.target
+"""
+        if os.access(os.path.dirname(fn), os.W_OK):
+            print(f"writing [{fn}]: ...")
+            with open(fn, "w") as f: 
+                f.write(out)
+            print(f"writing [{fn}]: done.")
+            print("""
+while in your project type `podman-compose systemd -a register`
+""")
+        else:
+            print(out)
+            log(f"Could not write to [{fn}], use 'sudo'")
+
+
 @cmd_run(podman_compose, "pull", "pull stack images")
 def compose_pull(compose, args):
     img_containers = [cnt for cnt in compose.containers if "image" in cnt]
@@ -2437,6 +2498,15 @@ def compose_logs_parse(parser):
         "services", metavar="services", nargs="*", default=None, help="service names"
     )
 
+@cmd_parse(podman_compose, "systemd")
+def compose_systemd_parse(parser):
+    parser.add_argument(
+        "-a",
+        "--action",
+        choices=["register", "create-unit"],
+        default="register",
+        help="create systemd unit file or register compose stack to it",
+    )
 
 @cmd_parse(podman_compose, "pull")
 def compose_pull_parse(parser):