External processes with environment variables

hi
I want to launch a nodejs service before my test suite starts and kill it when it has finished.
I am really struggling with Port.open, System.cmd etc… with passing environment variables.

In bash, I would do this :-

PORT=4001 yarn start

but i cannot work out how to do that in elixir and also be able to kill it when done (I was assuming i could get a pid or something - as in an OS pid, not an erlang pid).

Any help greatly appreciated

Cheers

Gary

The BEAM/Erlang/Elixir-way is to open a port with the command (you can pass env vars by hoisting into a shell call for example) then just hold the port passing data back and forth if necessary (or not if not), then just close the port when done (which gets closed with the BEAM system shutting down too).

Closing the port closes the pipe between the processes, which is stdin to the other process. Any process that correctly obeys the POSIX standard will either fork when opened to persist, or will die when it’s stdin closes (after cleaning up of course).

However a lot of node programmers do not like to follow the standards and like to live like zombies, not forking, not closing when stdin closes, nothing, so you get stuff like what you are experiencing now.

I’d probably just write a shell script and have the port open that, just have the shell script call yarn start with whatever env vars (that you could pass in even), background it, get it’s PID ($!) and have it just ‘wait’ on its stdin (cat is perfectly fine for this) and when it closes (cat would exit) then just kill the pid that you saved earlier and be done.

Blame this on node authors not following the standards properly.

Or you can use something like erlexec to open the node process and have ‘it’ send a kill signal when done. it is an easy to use library that uses ports and a runner program to manage external programs like yarn that are not programmed correctly. :wink:

1 Like

Thanks @OvermindDL1 - Im going to try erlexec first.

1 Like

I tried both - no joy im afraid :frowning:

I was a bit lost as to how to even use erlexec - I added it to my mix.exs file but when I did ‘iex -S mix’ - it would not start as erlexec was not started. I then tried adding it to the application as an extra child app to start but still the same so I gave up.

I did a shell script to start the server which worked, but could not be killed. So, when I killed the pid that it returned, it never killed the processes that the shell script had started.

This is my shell script

#!/bin/bash
cd ../app/easy_life_ui
PORT=4001 yarn start > /dev/null &
echo $!

What precise error did you get? erlexec worked fine for me so…

You could also try Porcelain, it is basically a more standard elixir erlexec with not quite as many features, for the control you need you will probably need to use it’s extended driver as well.

This will not kill it ever, and yeah it may not die because it has a child process pending.

Maybe something more like?

#!/bin/bash
cd ../app/easy_life_ui
PORT=4001 yarn start > /dev/null &
IPID=$!
cat # This waits until stdin closes
kill $IPID
sleep 1
kill -9 $IPID

Or so?

After looking a bit deeper, the error with erlexec was clear - it was

Not allowed to run as root without setting effective user (-user option)!

but at the time I just saw 'Could not start application {:not_running, :erlexec}

Ill try the cat solution you provided - thanks for that :slight_smile:

Gary

1 Like

Oooo yeah you never want to run a server as root, not even a BEAM/Erlang/Elixir server! Never run anything as root if at all possible! o.O

I like that it detects that and prevents it, great for security!

Its docker thats why - I think most things run as root inside the container.

Even in docker I don’t run things as root!

There have been security issues in docker sandboxes where root processes in the docker sandbox can escape to root on the host system (ditto in VM’s like VMWare and such too!). Regardless of your decision of sandbox you should still never run as root.

If you really want a more secure sandbox then use Illumos Zones as they permeate the kernel, but even then I’d still not use root! ^.^;

Why should a library responsible to run commands decide not to support running commands as root?

In my specific case using erlexec, I can’t run tests in the normal GitLab CI stack using the official elixir docker images since this library just decides to error on me instead. That makes no sense and there should be at least an option to disable that patronizing behavior.

1 Like

There are significant security issues running things as root, even in containers. It does make sense to allow an override though, PR it?

There are significant security issues running things as root

As long as we’re talking about a deployment, I fully agree. For the CI, the benefit of using official images without having to modify them over weighs in my case.

PR it?

The issue has been raised multiple times and the author doesn’t seem to like the idea very much. I had the problem with a dependency of a library I wrote myself. It was easier to just remove the erlexec dependency there.

For anyone seeing this thread, I can recommend using rambo for the job. https://hex.pm/packages/rambo
It doesn’t have the same amount of features, but for most use cases it’s a very nice and easy library.