podman-compose
podman-compose copied to clipboard
Missing support for variable interpolation in .env files
podman-compose deviates from docker-compose in
- that it does not interpolate strings in .env files, and
- that it does not know about host env vars in .env (like $HOME, $PWD etc)
To Reproduce
FILE: .env
A=bar
B=foo$A
C=$HOME/foo
D="${XDG_DATA_HOME:-$HOME/.local/share}/blabla"
FILE: docker-compose.yml
version: "4"
services:
variables:
image: busybox
command: ["/bin/busybox", "sh", "-c", "export | grep EXAMPLE"]
environment:
EXAMPLE_SIMPLE: "$B"
EXAMPLE_HOSTVAR_SIMPLE: $C
EXAMPLE_HOSTVAR_COMPLEX: $D
Commands
$ podman-compose config
Expected behavior
All the variables should be substituted as you would expect. Note that docker-compose config
just works as expected.
Also no need to explicitly pass around variables with -e from when invoking docker-compose.
Actual behavior Actual behaviour is that $B evaluates to literally foo$A, meaning it doesn't interpolate its value. $C and $D results in errors, as podman-compose doesn't know about $HOME at all.
Output
$ podman-compose version
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 4.5.0
podman-compose version 1.0.6
podman --version
podman version 4.5.0
Environment: Linux 6.1.31-2
Additional context
Current tests in repo do not test equality of output between docker-compose and podman-compose. That would possibly make your life easier to keep feature parity.
I can confirm this. It's easy to reproduce with the docker.io/postgres
image and a sample env
file like this:
DB_NAME=databasename
DB_USER=databaseuser
DB_PASS=databasepassword
POSTGRES_DB=${DB_NAME}
POSTGRES_USER=${DB_USER}
POSTGRES_PASSWORD=${DB_PASS}
Something like this causes postgres to fail with the following error in podman-compose logs
:
FATAL: invalid character in extension owner: must not contain any of ""$'\"
This issue also seems very similar to https://github.com/containers/podman-compose/issues/721, they might be duplicates.
Meanwhile, using the following in the compose.yaml
with --env-file
works just fine.
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
I'm also seeing that complex variables from the .env file like ${SAMPLE:-$PWD} also do not get the $PWD portion resolved in the yaml compose file, such as for volume mounting.
AFAICT the .env
-file is processed in two ways:
- Using the
python-dotenv
-library (which supports expansions within the.env
-file; i.e. any expansion can be reused withincompose.yaml
). - As an
--env-file
argument passed to podman when starting the pods (this will not resolve any inline expansions within the.env
-file, so a work-around is to manually do this incompose.yaml
as stated above).
I'm also seeing that complex variables from the .env file like ${SAMPLE:-$PWD} also do not get the $PWD portion resolved in the yaml compose file, such as for volume mounting.
Ran into this a moment ago. Didn't run into exactly this, but the equivalent thing in compose.yml
where environment variable replacement is already supported. Wanted to write something like this - ${OVERRIDE_DIR:-${HOME}/some/dir}:/other/dir:ro,z
but it was only interpolated once into - ${OVERRIDE_DIR:-/home/me/some/dir}:/other/dir:ro,z
.
I took a look at the source code, and it seems like there is no recursion to handle nested interpolation (which is part of the spec):
https://github.com/containers/podman-compose/blob/d704622522c3b4e517b084ad87bd4a15a29311c3/podman_compose.py#L267-L282
Should be a very easy fix though; will submit a PR in these few days.
Edit 1: After some initial experimentation, it seems like I was overly optimistic in assessing the difficulty of this task. It's pretty evident to me now that it would be practically impossible to implement a reliable nested interpolation parser without bringing in a proper lexer dependency (or worse, raw dogging one ourselves). Regex simply isn't cutting it. I'll continue to work on it but don't expect anything quick.
Edit 2: After looking away for a few days and coming back in a less sleep-deprived state, I am finding the problem slightly less intimidating but still quite tricky. I think I will be able to make a regex-based POC but there are some unresolved questions, mainly regarding ambiguities in the interpolation spec. We can discuss them in the PR.
Edit 3: While writing the PR comments, the more I wrote the more problems I found, and it is quickly spiralling out of control. Recursive string replacement is just not going to be nearly good enough, because fundamentally we cannot distinguish between a lexical token (e.g. $$
) and a concatenated string (e.g. $$${FOO}
where ${FOO}
is $
-> $$
). And if we start doing structured replacements, we might as well be writing an AST (which I'm certainly not doing in Python). So I guess I won't be submitting a PR. Sorry. ¯\_(ツ)_/¯
I hit this today with something like:
environment:
- DATABASE_USER=${DATABASE_USER:-postgres}
- DATABASE_URL=${DATABASE_URL:-postgresql://$DATABASE_URL:somepassword@host/database}
No variation of the last DATABASE_URL
(eg. $DATABASE_URL
or ${DATABASE_URL}
) gets resolved, it always stays as a literal postgresql://$DATABASE_URL:somepassword@host/database