containers / podlet

Generate Podman Quadlet files from a Podman command, compose file, or existing object
https://crates.io/crates/podlet
Mozilla Public License 2.0
422 stars 12 forks source link

Add option to convert relative host paths into absolute paths #52

Closed rugk closed 8 months ago

rugk commented 8 months ago

Problem: If not given, podman seems to assume $HOME/.config/containers/systemd as your home directory. This is bad if you use any relative paths in your compose file (e.g. I assume).

STR

When you read this: Pay attention to the places ./config appears. ./config is a configuration directory with lots of stuff in there, but could be anything (like a file) for the sake.

Have this docker-compose.yml YAML:

version: "2.1"
services:
  grocy:
    image: lscr.io/linuxserver/grocy:latest
    container_name: grocy
    environment:
      # - PUID=1000
      # - PGID=1000
      - TZ=Europe/Berlin
    volumes:
      - ./config:/config:Z
    ports:
      - 9283:80
    restart: unless-stopped
    labels:
      - io.containers.autoupdate=registry

Do all the processing chain to get it into a service and try to start it:

$ podlet -f "$HOME/.config/containers/systemd" --install compose docker-compose.yml
systemctl --user daemon-reload
systemctl --user start service-name
systemctl --user status service-name

Debugging

When you look into the log, you'll see (apart from other stuff) this:

$ journalctl --user -xeu grocy.service
**snip**
*** *** grocy[70221]: Error: statfs ***/.config/containers/systemd/config: no such file or directory
**snip**

Now where does that strange ***/.config/containers/systemd/config come from? Tip: It has nothing to do with system.

$ /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
quadlet-generator[70732]: Loading source unit file *****/.config/containers/systemd/grocy.container
---grocy.service---
[X-Container]
AutoUpdate=registry
ContainerName=grocy
Environment=TZ=Europe/Berlin
Image=lscr.io/linuxserver/grocy:latest
PublishPort=9283:80
Volume=./config:/config:Z

[Service]
Restart=always
Environment=PODMAN_SYSTEMD_UNIT=%n
KillMode=mixed
ExecStop=/usr/bin/podman rm -f -i --cidfile=%t/%N.cid
ExecStopPost=-/usr/bin/podman rm -f -i --cidfile=%t/%N.cid
Delegate=yes
Type=notify
NotifyAccess=all
SyslogIdentifier=%N
ExecStart=/usr/bin/podman run --name=grocy --cidfile=%t/%N.cid --replace --rm --cgroups=split --sdnotify=conmon -d -v *****/.config/containers/systemd/config:/config:Z --label io.containers.autoupdate=registry --publish 9283:80 --env TZ=Europe/Berlin lscr.io/linuxserver/grocy:latest

[Install]
WantedBy=default.target

[Unit]
SourcePath=*****/.config/containers/systemd/grocy.container
RequiresMountsFor=%t/containers
RequiresMountsFor=*****/.config/containers/systemd/config

The solution: ***/.config/containers/systemd/config is our ./config from above, apparently!

Somehow the podman-systemd-generator or so always assumes $HOME/.config/containers/systemd as it's home directory.

Proposed solution

Always include WorkingDir=… with the absolute path to the $PWD when podlet is executed, to make sure it works.

Workaround

I guess adding -w "$PWD" respectively working_dir: $PWD should solve it, too.

Edit: Ah no working_dir seems to be the location inside the container, not the one for podman itself, i.e. when creating volumes.

System

$ podlet --version
podlet 0.2.3 # (from quay.io)
$ podman --version
podman version 4.7.2

Fedora CoreOS v39.20231204.3.3

Misc

AFAIK this should also affect your Readme caddy example, which also uses ./Caddyfile and ./caddy_data.

k9withabone commented 8 months ago

Podlet does a literal translation when creating quadlet files. If you look at the quadlet docs, in the [Container] section, the docs for the Volume= option say that relative paths are resolved relative to the location of the unit file.

A --canonicalize-system-paths option could be added which transforms system paths (like directories for volumes and device mounts) into their canonical form based on the current working directory when podlet is run. However, this would require that the file exists, see std::fs::canonicalize().

rugk commented 8 months ago

Ah thanks, I see, but the “location of the unit file” changes, during that setup, which breaks it in my case.

--canonicalize-system-paths

A good idea, however, if the file needs to exist maybe not optimal then?

How about an option --expand-pwd or so that just expands the ./local file paths to the one where the docker-compose file lies? Because this would be the 100% (compatible) equivalent to when you run docker compose. There files neither need to exist and they are relative to the path of the docker-compose file.

After all podman does not need a full canonical (real) path (it can have ../ in there), it just needs the correct PWD? A path /home/somename/absolutepath-to-docker-project/named-project/../other-name/config.yml is totally fine e.g. That's why my original proposal just involved trying to change the pwd.

k9withabone commented 8 months ago

What do you think of a podlet --absolute-host-paths option whose --help reads as follows:

--absolute-host-paths []

Convert relative host paths to absolute paths

Relative host paths in generated quadlet files are resolved using the given directory, the current working directory, or, for podlet compose, the parent directory of the given compose file.

All host paths are also cleaned to remove interior /../, /./, and //.

Returns an error if the current working directory cannot be read or if the given directory path is not absolute.

Then the implementation uses something like this with the path-clean crate:

use std::{
    borrow::Cow,
    path::{Path, PathBuf},
};

use path_clean::PathClean;

fn absolute_clean_path(current_dir: &Path, path: &Path) -> PathBuf {
    let path: Cow<Path> = if path.is_absolute() {
        path.into()
    } else {
        current_dir.join(path).into()
    };

    path.clean()
}