Support arbitrary interpolation of compose files #492

Open
opened 2025-01-23 02:07:46 +00:00 by marlon · 12 comments

When trying to substitute an env var for a value in a recipe config file, abra sometimes gives an error like:

FATA[0000] unable to validate recipe: error while interpolating services.ssh.ports.[].published: failed to cast to expected type: strconv.Atoi: parsing "": invalid syntax caller="/drone/src/cli/internal/validate.go:71 ValidateRecipe" stack="/drone/src/cli/internal/validate.go:71

Steps to reproduce:

Create a recipe with a compose file that includes, for example, a section with

    ports:
      - target: 22
        published: ${PORT}

Run abra app new mynewrecipe

Get error

When trying to substitute an env var for a value in a recipe config file, abra sometimes gives an error like: `FATA[0000] unable to validate recipe: error while interpolating services.ssh.ports.[].published: failed to cast to expected type: strconv.Atoi: parsing "": invalid syntax caller="/drone/src/cli/internal/validate.go:71 ValidateRecipe" stack="/drone/src/cli/internal/validate.go:71` ### Steps to reproduce: Create a recipe with a compose file that includes, for example, a section with ``` ports: - target: 22 published: ${PORT} ``` Run `abra app new mynewrecipe` Get error
Owner

Thanks for the report @marlon ! I'm not super familiar with Go but it seems Atoi is trying to cast to an integer; maybe it would help to provide a default e.g. published: ${PORT:-22}?

Thanks for the report @marlon ! I'm not super familiar with Go but it seems `Atoi` is trying to cast to an integer; maybe it would help to provide a default e.g. `published: ${PORT:-22}`?
Owner

Yeh @marlon I think @3wordchant hit the nail on the head here. Also you can check that you're threading the PORT=... through in the .env file also?

Yeh @marlon I think @3wordchant hit the nail on the head here. Also you can check that you're threading the `PORT=...` through in the `.env` file also?
decentral1se added the
question
label 2025-02-05 15:22:33 +00:00
Owner

In the same spirit as #525 I think we should try to catch these errors and reinterpret them to something that someone who doesn't know internals can make sense of. See #492 (comment).

~~In the same spirit as https://git.coopcloud.tech/toolshed/abra/pulls/525 I think we should try to catch these errors and reinterpret them to something that someone who doesn't know internals can make sense of.~~ See https://git.coopcloud.tech/toolshed/abra/issues/492#issuecomment-25959.
decentral1se added
enhancement
and removed
question
labels 2025-03-23 09:42:24 +00:00
decentral1se changed title from valid env variable substitution in compose file throws an error to Improve compose validation errors 2025-03-23 09:42:51 +00:00
decentral1se added this to the Abra v0.10.1 project 2025-04-20 05:48:30 +00:00
decentral1se moved this to Backlog in Abra v0.10.1 on 2025-04-20 06:06:40 +00:00
decentral1se moved this to Backlog in Abra v0.10.1 on 2025-04-21 19:08:15 +00:00
decentral1se moved this to Backlog in Abra v0.10.1 on 2025-04-21 19:08:25 +00:00
decentral1se moved this to Backlog in Abra v0.10.1 on 2025-04-24 08:58:35 +00:00
decentral1se moved this to Backlog in Abra v0.10.1 on 2025-04-24 08:58:37 +00:00
decentral1se moved this to Backlog in Abra v0.10.1 on 2025-04-24 08:58:38 +00:00
decentral1se removed this from the Abra v0.10.1 project 2025-04-24 09:00:51 +00:00
decentral1se added this to the Abra v0.11.x project 2025-04-24 19:38:01 +00:00
decentral1se moved this to Backlog in Abra v0.11.x on 2025-05-11 08:24:52 +00:00
decentral1se self-assigned this 2025-08-18 07:39:33 +00:00
Owner

Coming back to this, I'm not sure we should actually do anything about this in abra. The original error was a recipe configuration issue. The error message was "good enough" to point in the right direction. I think there is always a tension between making things more usable and letting the underlying errors bubble up. I am not sure where I would hook in to this really long error message to smooth it over (we typically match by string contents...) and furthermore, I would fear that we'd end up suppressing other messages which might match.... gonna bail on this for now, please re-open if you feel strongly otherwise.

Coming back to this, I'm not sure we should actually do anything about this in `abra`. The original error was a recipe configuration issue. The error message was "good enough" to point in the right direction. I think there is always a tension between making things more usable and letting the underlying errors bubble up. I am not sure where I would hook in to this really long error message to smooth it over (we typically match by string contents...) and furthermore, I would fear that we'd end up suppressing other messages which might match.... gonna bail on this for now, please re-open if you feel strongly otherwise.
decentral1se moved this to Done in Abra v0.11.x on 2025-08-18 19:02:51 +00:00
Author

Sorry to have left this stale for so long, but the issue I was trying to surface here is that the original config snippet I gave is valid compose syntax, but abra seems to be throwing an error when it parses it.

An alternate formulation of the issue is, when I use this syntax:

    ports:
      - ${PORT:-2220}:22

I get an error:
FATA services.ssh.ports.0 Does not match format 'ports'

Is it correct that abra should not allow variable substitution for ports? Is it failing to interpolate before testing the syntax? I don't fully understand but I know that when I run a compose file using this syntax directly using docker compose it works fine. So I infer that it's an abra issue, but maybe I'm missing something.

Sorry to have left this stale for so long, but the issue I was trying to surface here is that the original config snippet I gave is valid compose syntax, but abra seems to be throwing an error when it parses it. An alternate formulation of the issue is, when I use this syntax: ``` ports: - ${PORT:-2220}:22 ``` I get an error: `FATA services.ssh.ports.0 Does not match format 'ports'` Is it correct that abra should not allow variable substitution for ports? Is it failing to interpolate before testing the syntax? I don't fully understand but I know that when I run a compose file using this syntax directly using docker compose it works fine. So I infer that it's an abra issue, but maybe I'm missing something.
marlon reopened this issue 2025-10-06 17:02:59 +00:00
Owner

@marlon all good. can you share the full compose config? I'm a bit suspicious of services.ssh.ports and what that is doing? In general, I don't think any config specifies ports that is outside of the traefik labels? E.g the gitea config below. This could be an abra issue, but I think it might not be a working config / approach that we've used before, which is maybe why you're running into it?

989294173e/compose.yml (L84)

@marlon all good. can you share the full compose config? I'm a bit suspicious of `services.ssh.ports` and what that is doing? In general, I don't think any config specifies ports that is outside of the traefik labels? E.g the gitea config below. This could be an `abra` issue, but I think it might not be a working config / approach that we've used before, which is maybe why you're running into it? https://git.coopcloud.tech/coop-cloud/gitea/src/commit/989294173e63e78daa6fd45bb93b4d51d918787d/compose.yml#L84
Author

You're right that what I'm attempting with this syntax is experimental compared to the conventional approach in coop cloud to handle ports through Traefik labels. The big thing I'm investigating is whether there's a coop cloud-compatible way to assign port mappings based on .env configuration, rather than hardcode them in recipes. This is relevant because the recipe-defined approach complicates running, for example, two SFTP servers behind the same ingress.

I am interested generally in thoughts about the broader experiment, but for the purposes of this syntax issue, I guess the main question is whether variable substitution doesn't work intentionally for some design reason, or whether it's a bug? I had initially thought that maybe it was just expected behavior from docker, but like I said it works in docker compose...

Anyway here's the config I was experimenting with:

version: "3.8"
services:
  ssh:
    image: lscr.io/linuxserver/openssh-server:latest
    networks:
      - proxy
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - USER_NAME=sftp
      - PUBLIC_KEY
    volumes:
      - content:/content:rw
    ports:
      - ${PORT:-2220}:22
    deploy:
      restart_policy:
        condition: on-failure
  # The following is an admittedly hacky way of setting the owner
  # of the `content` volume to the unprivileged `sftp` user, so
  # that content can be transferred through the unprivileged sshd process
  # using `scp` etc.
  sshstart:
    image: lscr.io/linuxserver/openssh-server:${TAG}
    user: root
    depends_on:
      - ssh
    deploy:
      restart_policy:
        condition: none
    volumes:
      - content:/content:rw
    entrypoint: [ "bash", "-c", "sleep 10 && chown -R 1000:1000 /content"]

volumes:
  content:

networks:
  proxy:
    external: true

(Note that substitution works for ${TAG} in the image declaration, but not for the port)

You're right that what I'm attempting with this syntax is experimental compared to the conventional approach in coop cloud to handle ports through Traefik labels. The big thing I'm investigating is whether there's a coop cloud-compatible way to assign port mappings based on .env configuration, rather than hardcode them in recipes. This is relevant because the recipe-defined approach complicates running, for example, two SFTP servers behind the same ingress. I am interested generally in thoughts about the broader experiment, but for the purposes of this syntax issue, I guess the main question is whether variable substitution doesn't work intentionally for some design reason, or whether it's a bug? I had initially thought that maybe it was just expected behavior from docker, but like I said it works in docker compose... Anyway here's the config I was experimenting with: ``` version: "3.8" services: ssh: image: lscr.io/linuxserver/openssh-server:latest networks: - proxy environment: - PUID=1000 - PGID=1000 - TZ=Etc/UTC - USER_NAME=sftp - PUBLIC_KEY volumes: - content:/content:rw ports: - ${PORT:-2220}:22 deploy: restart_policy: condition: on-failure # The following is an admittedly hacky way of setting the owner # of the `content` volume to the unprivileged `sftp` user, so # that content can be transferred through the unprivileged sshd process # using `scp` etc. sshstart: image: lscr.io/linuxserver/openssh-server:${TAG} user: root depends_on: - ssh deploy: restart_policy: condition: none volumes: - content:/content:rw entrypoint: [ "bash", "-c", "sleep 10 && chown -R 1000:1000 /content"] volumes: content: networks: proxy: external: true ``` (Note that substitution works for ${TAG} in the image declaration, but not for the port)
Owner

I see, that makes sense! OK I will investigate if abra is causing this (seems likely now) since on docker compose works... thanks for the info! Had read through this also: https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/

I see, that makes sense! OK I will investigate if `abra` is causing this (seems likely now) since on `docker compose` works... thanks for the info! Had read through this also: https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/
decentral1se modified the project from Abra v0.11.x to Abra v0.12 2025-10-07 19:46:00 +00:00
decentral1se added
bug
and removed
enhancement
labels 2025-10-07 19:46:09 +00:00
decentral1se moved this to Prioritised in Abra v0.12 on 2025-10-15 08:03:32 +00:00
decentral1se moved this to Backlog in Abra v0.12 on 2025-10-17 17:31:31 +00:00
decentral1se moved this to Prioritised in Abra v0.12 on 2025-10-17 17:31:52 +00:00
decentral1se started working 2025-10-23 15:47:44 +00:00
Owner

Woah, this is a bug that cannot be easily fixed. It seems that supporting arbitrary interpolation will require a significant refactor. abra has more operating modes than docker compose and docker swarm. We sometimes have an app env and sometimes not (e.g. when linting a recipe in isolation). Some values in the compose file require special validation and if the value is not present, then it explodes. So, some code paths don't have access to PORT=2222 even tho it's in the app env. I'm not sure how to fix this right now.

Woah, this is a bug that cannot be easily fixed. It seems that supporting arbitrary interpolation will require a significant refactor. `abra` has more operating modes than `docker compose` and `docker swarm`. We sometimes have an app env and sometimes not (e.g. when linting a recipe in isolation). Some values in the compose file require special validation and if the value is not present, then it explodes. So, some code paths don't have access to `PORT=2222` even tho it's in the app env. I'm not sure how to fix this right now.
decentral1se worked for 3 hours 23 minutes 2025-10-23 19:11:07 +00:00
Author

🤔🤔 Well thanks for looking into it and the explanation!

🤔🤔 Well thanks for looking into it and the explanation!
decentral1se removed this from the Abra v0.12 project 2025-10-25 08:05:38 +00:00
decentral1se changed title from Improve compose validation errors to Support arbitrary interpolation of compose files 2025-10-25 08:06:03 +00:00
Owner

@marlon yeh I guess the work-around for now is to create some hard-coded values in separate compose.foo.yml files 🤔

I would like to carry out this refactor to have full feature parity but it's not super high on my stack atm and also might be a bit of a gruesome refactor. So, I'd ideally punt this down the road a bit until other stuff getting in peoples way are sorted (new v0.12/13).

Good that you caught this one. It highlights some technical debt we need to pay off 🙈

@marlon yeh I guess the work-around for now is to create some hard-coded values in separate `compose.foo.yml` files 🤔 I would like to carry out this refactor to have full feature parity but it's not super high on my stack atm and also might be a bit of a gruesome refactor. So, I'd ideally punt this down the road a bit until other stuff getting in peoples way are sorted (new v0.12/13). Good that you caught this one. It highlights some technical debt we need to pay off 🙈
Author

Great, yeah agreed it doesn't seem very high priority but I think resolving it eventually could unlock some useful recipe design patterns.
One day I will learn Go and contribute more than just bug reports lol

Great, yeah agreed it doesn't seem very high priority but I think resolving it eventually could unlock some useful recipe design patterns. One day I will learn Go and contribute more than just bug reports lol
Sign in to join this conversation.
3 Participants
Notifications
Total Time Spent: 3 hours 23 minutes
decentral1se
3 hours 23 minutes
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: toolshed/abra#492
No description provided.