Page MenuHomePhabricator

[builds-builder,jobs-api,upstream] Calling nontrivial Procfile commands with arguments results in confusing error (“no such file or directory”)
Open, Stalled, MediumPublic

Description

According to the build service documentation (permalink),

You could also pass additional arguments, for example --command "migrate --production" would run the script specified in Procfile with the --production argument.

However, as far as I can tell from some testing, this only works if the Procfile command just names another command with nothing else, not even any arguments. I tested this with:

Procfile
print-appended-arguments-per-line: printf >| "$TOOL_DATA_DIR/print-appended-arguments-per-line" '%s\n'
print-shell-arguments-per-line: printf >| "$TOOL_DATA_DIR/print-shell-arguments-per-line" '%s\n' "$@"
run-true: true
run-touch: touch
run-touch-hardcoded-and-more: touch /tmp/hardcoded

run-true and run-touch work, but the other three commands fail with quite confusing errors:

$ sudo docker run --rm -it --entrypoint='launcher' tools-harbor.wmcloud.org/tool-lucaswerkmeister-test/tool-lucaswerkmeister-test:latest bash


heroku@4702f3006738:/workspace$ run-true; echo $?; echo; echo
0


heroku@4702f3006738:/workspace$ run-touch /tmp/abc
heroku@4702f3006738:/workspace$ run-touch-hardcoded-and-more /tmp/def
touch /tmp/hardcoded: line 1: touch /tmp/hardcoded: No such file or directory
heroku@4702f3006738:/workspace$ ls /tmp; echo; echo
abc


heroku@4702f3006738:/workspace$ mkdir /tmp/datadir
heroku@4702f3006738:/workspace$ export TOOL_DATA_DIR=/tmp/datadir
heroku@4702f3006738:/workspace$ print-appended-arguments-per-line
heroku@4702f3006738:/workspace$ cat /tmp/datadir/print-appended-arguments-per-line 

heroku@4702f3006738:/workspace$ print-appended-arguments-per-line arg1
printf >| "$TOOL_DATA_DIR/print-appended-arguments-per-line" '%s\n': line 1: printf >| /tmp/datadir/print-appended-arguments-per-line '%s\n': No such file or directory
heroku@4702f3006738:/workspace$ print-shell-arguments-per-line 
heroku@4702f3006738:/workspace$ cat /tmp/datadir/print-shell-arguments-per-line 

heroku@4702f3006738:/workspace$ print-shell-arguments-per-line arg1
printf >| "$TOOL_DATA_DIR/print-shell-arguments-per-line" '%s\n' "$@": line 1: printf >| /tmp/datadir/print-shell-arguments-per-line '%s\n' arg1: No such file or directory

If this is the case, then it means that migrate --production as documented on Wikitech also wouldn’t work correctly, because migrate was earlier on the page defined as python3 -m app.migrate, i.e. a command with arguments. (But I haven’t tested this command in particular.)

Waiting on upstream

This is actually something that comes from upstream https://github.com/heroku/procfile-cnb/issues/151, the issue comes from the spec of the launcher process (https://github.com/buildpacks/spec/blob/main/platform.md#launcher), and the non-backwards compatible changes introduced when stopping to wrap every command in a shell.

Waiting on upstream to upgrade to adapt to the new processes (that might still not allow mixing procfile defined arguments and user supplied ones, but we'll see).

Event Timeline

It’s worth noting that, when the commands aren’t called with arguments, the shell syntax seen in the two print-* commands is fully supported; in fact, commands can be full scripts, including (in Bash terms) simple commands, pipelines, lists, compound commands, and function definitions (and I’ve also been able to make good use of this in T320140, so I’d like to keep it this way). In light of this, I feel like the model of print-shell-arguments-per-line would be more suitable than the one of print-appended-arguments-per-line, if we have a choice: make the arguments (if any) available as "$@", to be used anywhere in the command, instead of just appending them at the end. (But of course that breaks compatibility with trivial commands as they currently work. And if you really need to use "$@" in non-terminal position, you can always wrap your command like myfun() { some-command "$@" other-argumests; }; myfun, assuming function definitions continue to work.)

(Note: At the moment T320140 isn’t actually blocked on this, as I’m using Kubernetes directly. If I move to Toolforge Jobs, and the jobs are limited to the Procfile commands, then this would become more of an issue.)

(Note: At the moment T320140 isn’t actually blocked on this, as I’m using Kubernetes directly. If I move to Toolforge Jobs, and the jobs are limited to the Procfile commands, then this would become more of an issue.)

Thanks for the note, I'll try to prioritize looking into this, I do suspect though that there might be some limitation on how the launcher process parses arguments, have to read https://github.com/buildpacks/spec/blob/main/platform.md#launcher in detail.

I think that the issue comes from the procfile buildpack, it's generating a metadata.toml file with processes like:

[[processes]]
  type = "inline-param"
  command = "./test.sh inline-param"
  args = []
  direct = false
  buildpack-id = "heroku/procfile"

[[processes]]
  type = "no-params"
  command = "./test.sh"
  args = []
  direct = false
  buildpack-id = "heroku/procfile"

[[processes]]
  type = "shell-params"
  command = "./test.sh \"$@\""
  args = []
  direct = false
  buildpack-id = "heroku/procfile"

[[processes]]
  type = "split-shell-params"
  command = "./test.sh \"$1\" \"$2\""
  args = []
  direct = false
  buildpack-id = "heroku/procfile"

[[processes]]
  type = "web"
  command = "uwsgi uwsgi.ini"
  args = []
  direct = false
  buildpack-id = "heroku/procfile"

And I suspect that instead of having a command with everything, it should have moved the arguments to args, looking

dcaro changed the task status from Open to In Progress.Feb 14 2024, 1:23 PM
dcaro claimed this task.
dcaro moved this task from Next Up to In Progress on the Toolforge (Toolforge iteration 05) board.

It's actually quite tricky, and upstream might take a bit to fix it: https://github.com/heroku/procfile-cnb/issues/151

So, to clarify myself, current behavior is:

  • If no parameters are passed to the binary created from the Procfile (/cnb/process/<procfile-entry>), it will wrap the command in a shell (bash -c) and pass to it the command as one parameter.
  • If you pass any other parameters, it will not wrap the command in a shell, but instead, try to execute it as one binary (thus, not finding it if it it has any parameters defined), and will pass the extra args the user passed to the binary.

So, besides pushing that upstream task or re-implementing the buildpack ourselves, we can work around the issue by createing a wrapper script, like:

#!/bin/bash
## run-cron.sh

mycommand hardcoddedarg "$@"

And a Procfile entry like (note that we are not passing any arguments at all):

run-cron: ./run-cron.sh

This allows passing extra args (and other more advanced patterns if needed).

@LucasWerkmeister I have updated the docs here https://wikitech.wikimedia.org/wiki/Help:Toolforge/Build_Service#Example%3A_Python_web_service, with the script workaround, we can leave this task open until upstream fixes the issue, but might take a while, so I'd recommend using the workaround for now.

dcaro changed the task status from In Progress to Stalled.Feb 14 2024, 2:27 PM
dcaro updated the task description. (Show Details)
dcaro triaged this task as Medium priority.Feb 19 2024, 3:55 PM
dcaro changed the task status from Stalled to In Progress.Feb 20 2024, 2:28 PM
dcaro renamed this task from Build service: Calling nontrivial Procfile commands with arguments results in confusing error (“no such file or directory”) to [builds-builder,jobs-api] Calling nontrivial Procfile commands with arguments results in confusing error (“no such file or directory”).Mar 5 2024, 1:13 PM
dcaro changed the task status from In Progress to Stalled.
dcaro removed a project: Toolforge Build Service.
dcaro renamed this task from [builds-builder,jobs-api] Calling nontrivial Procfile commands with arguments results in confusing error (“no such file or directory”) to [builds-builder,jobs-api,upstream] Calling nontrivial Procfile commands with arguments results in confusing error (“no such file or directory”).Mon, Apr 8, 1:10 PM