How to pass command line arguments to Mix Release built - erlang

I am trying to pass command-line arguments to my elixir release. I have built the release using
MIX_ENV=prod mix release
Now, Am not able to pass any command-line arguments with the start command.
_build/prod/rel/prod/bin/prod start arg1 arg2
Using eval i have achieved passing the arguments but it stops after a while.
_build/prod/rel/prod/bin/prod eval "Hello.nodes([3, :node1])"
Is there any way that I can pass the args through the start flag?

Using eval i have achieved passing the arguments but it stops after a
while.
_build/prod/rel/prod/bin/prod eval "Hello.nodes([3, :node1])"
From the docs:
The eval command starts its own instance of the VM but without
starting any of the applications in the release and without starting
distribution. For example, if you need to do some prep work before
running the actual system, like migrating your database, eval can be a
good fit. Just keep in mind any application you may use during eval
has to be explicitly loaded and/or started.
I'm guessing that your eval tries to use an application that you didn't explicitly load before executing the eval.
Is there any way that I can pass the args through the start flag?
It's not documented, but there are various ways to configure a release:
https://elixir-lang.org/getting-started/mix-otp/config-and-releases.html
Maybe an escript would be a better fit?
escript provides support for running short Erlang programs without
having to compile them first, and an easy way to retrieve the
command-line arguments.
It is possible to bundle escript(s) with an Erlang runtime system to make it self-sufficient and relocatable.

Related

Implement 'Entrypoint' like functionality in Cloud Native Buildpack

I have a multi-process web app. The processes are contributed by different buildpacks. The default process will start the web application. I have a use case in which a given shell script should be executed before the default process invocation.
I have tried the following approach;
Create a custom-buildpack
Create a script that needs to be executed and invoke the web process in it.
Create a new process based on the above shell sciprt by specifying it in launch.toml definition
Make the buildpack launchable
The entrypoint.sh
#!/usr/bin/env bash
# Some fancy stuff..
#Invoke the web process
/cnb/process/web
Create lauch.toml from the build script of custom-buildpack. Make the entrypoint process the default one.
cat > "$layers_dir/launch.toml" << EOL
[[processes]]
type = "entrypoint"
command = "bash"
args = ["$scriptlayer/bin/entrypoint.sh"]
default = true
EOL
echo -e '[types]\nlaunch = true' > "$layers_dir/assembly-scripts.toml"
Truncated pack inspect-image output
Processes:
TYPE SHELL COMMAND ARGS
entrypoint (default) bash bash /layers/gw_assembly-scripts/assembly-scripts/bin/entrypoint.sh
task bash catalina.sh run
tomcat bash catalina.sh run
web bash catalina.sh run
Is there any better CNB native approach to achieve this use case?
You have a couple of options here:
The simplest option would be to add a .profile script to the root of your application. It's a bash script, so anything you can write in bash can be done there, however, it's primarily for initializing your app and setting additional env variables.
This file runs prior to the command in your process type. I looked for documentation on this behavior, but only found it briefly mentioned in the buildpacks spec.
As an example, if I put .profile in the root of my application and inside that file, I write echo 'Hello World!'. I'll see Hello World! printed before any of my process types execute.
If you want to create a buildpack, you can achieve something similar to the .profile script by having your buildpack include an exec.d binary.
This is a binary that's part of your launch image and gets run prior to any of your process types. It allows you to take actions to initialize an application and set additional environment variables dynamically before your application starts.
This mechanism is often used by buildpack authors to provide dynamic behavior at runtime based on changes to environment variables or Kubernetes service bindings. For example, turning on/off features like APM tools, debugging, and metrics.
A few other miscellaneous notes.
Neither of the options above allows you to change the actual process type. The process type that will be executed is selected prior to these options (.profile and exec.d) running and you cannot influence that from within. You can only use them to run things prior to the process type running.
The buildpack spec does not allow for a buildpack to modify the process types for another buildpack. So you cannot create a buildpack that wraps or modifies process types set by another buildpack. That said, a buildpack can override the process types set by another buildpack. Buildpacks that are later in the order group will override earlier buildpacks.
From the spec: A combined processes list derived from all launch.toml files such that process types from later buildpacks override identical process types from earlier buildpacks.
With buildpacks, the entrypoint is always the launcher. The launcher is a process that runs and implements the application side of the buildpack specification. It runs .profile, exec.d binaries, sets up buildpack provide environment variables and eventually launch the specified process type.
If you override the entrypoint for a container then the launcher won't run and none of the things it is supposed to do will happen. Sometimes this is desired, like if you're troubleshooting, but usually you want the launcher to be the entrypoint.

How to set bash environment variables using lua

I am new to lua script features.
I tried using,
os.execute("export MY_VAR=10")
io.popen("export MY_VAR=10")
from lua script.
I try reading MY_VAR variable from shell using echo $MY_VAR after lua script is executed but I do not see MY_VAR getting set to 10.
How do we set the environment variable using lua script?
Your problem isn't a lua problem. Your problem is misunderstanding how process environments work.
Every time you run os.execute or io.popen you are running a new process with new environment.
So while you may be correctly setting MY_VAR in that processes environment (and it would affect any processes run as children processes of that process) it doesn't survive beyond the death of the launched process and so cannot be seen by any other processes.
If you want to affect the lua process's environment (which would then, in turn, affect the environment's of processes run by lua) then you need a binding to the setenv system function (which lua itself doesn't provide as it doesn't pass the clean C test that lua uses for things it includes).

How can I build an Elixir escript that does not halt the Erlang VM after execution (like elixir --no-halt)

I have a program that starts the application and then adds (children) workers to a supervisor. Obviously after doing only that it has nothing more left to do and it halts (exits). So making it not halt the VM would allow the workers to work.
The only solution I have came up was to add:
IO.gets "Working... To finish hit <Enter>."
at the end...
I want to build an escript that after running will not halt the Erlang VM just like:
elixir --no-halt -S mix run --eval 'MyApp.CLI.m
ain(["some-arg"])'
or
mix run --no-halt --eval 'MyApp.CLI.m
ain(["some-arg1,some-arg2"])'
Is there a way to do this with escript?
Or should I use a different solution to pack and distribute my program that is actually more like a server/daemon than a command line tool?
A typical approach to packaging such systems is an OTP release. You can use exrm for that.
If for some reasons, you still want to use escript, you can just call :timer.sleep(:infinity) after you start all the applications and processes.
NOTE: Starting from Elixir 1.9
We can use System.no_halt(true) to allow script to never stop.
Here is simple script example:
defmodule Mix.Tasks.NoHalt do
use Mix.Task
def run(_) do
System.no_halt(true)
IO.puts("Never die!")
end
end

passing runtime arguments to erlang when running rebar eunit

In my startup script, I am starting erlang with:
erl -args_file vm.args
Currently, while trying to run unit tests with rebar eunit is there a way for me to pass custom runtime arguments such as the -args_file option to the erlang process that rebar kicks off? I have searched docs high and low to no avail...
I appreciate the help.
I answered my own question. I use the ERL_FLAGS variable to pass command line args. Here is a snippet from my Makefile:
ERL_FLAGS="-args_file test/conf/vm.eunit.args" ./rebar skip_deps=true eunit
The first method is satisfied with your restriction:
1. in your eunit test function, to use erlang's "os:cmd("erl -name bar -detached arguments")." to start another erlang node. In the arguments, you can insert whatever you want. But eunit's check will be difficult because the test will cross the nodes.
The second methods is to change your restriction:
1. To use "configuremnt file" to pass arguments, and not to use command parameter to pass parameters.

Run erlang application without terminal depending

I have erlang application: *.app file and some *.erl files. I compile all of them. In terminal i start erl and there application:start(my_application)., all ok, but if i closed terminal application close too. How can i run application without terminal depending?
Thank you.
You likely want to use the -noshell option to erl. The syntax is
erl -noshell -s Module Function Arguments
So in your case, this might be
erl -noshell -s application start my_application
This should allow you (for example if you are on Unix/Linux) to start your application as a background process and leave it running.
One useful variation is to also call the stop/0 function of the init module so that the Erlang environment will stop when it has finished running your function. This comes in handy if you want to run a simple one-use function and pipe the output to some other process.
So, for example, to pipe to more you could do
erl -noshell -s mymodule myfunction -s init stop | more
Finally, you might also be able to use the escript command to run your Erlang code as scripts rather than compiled code if it makes sense for your situation.
Hope that helps.
The proper way to handle this situation, is building a release containing your app and running the system as so called embedded one.
This release is going to be completely independent (it will hold erts and all the libs like, kernel, std, mnesia etc.).
On start, the new process will not be connected to shell process.
It will be OS process, so you can attach to it with pipes. All script are included in OTP.
Here is some info: http://www.erlang.org/doc/design_principles/release_structure.html
It may seem to be complicated, but tools like rebar do everything for you.

Resources