Ping the riak node, print the response and halt the system.
erl -setcookie riak -name "${USER}-"@127.0.0.1 -noshell \
-eval "Res = net:ping('dev1@127.0.0.1'), io:put_chars(standard_error,io_lib:format(\"~p\",[Res]))." \
-eval "erlang:halt()."
As a result, pong
is printed to the console, the response from the remote node, we're ready to start executing Erlang.
That worked! But the command could do with some improving. I can't even break the line so it's readable at render time because I need that big quoted string. I'm going to be feeding some reasonbably large commands to the script so lets try to simplify what we're working with.
Firstly, it's awkward having to escape all the quotation commands in that command.
Lets use a here document to express the command, I got the detailed inspiration from Dennis Williamson on Stackoverflow
IFS='' read -r -d '' ERL_CMD <<'EOF'
Res = net:ping('dev1@127.0.0.1'),
io:put_chars(standard_error,io_lib:format("~p",[Res])).
EOF
That executes so much nicer, I don't like to have to structure long command lines.
erl -setcookie riak -name "${USER}-"@127.0.0.1 -noshell \
-eval $ERL_CMD -eval "erlang:halt()."
Lets look at improving the identifier, as it currently stands we cannot run more than one instance. It is reasonable to imagine that we will not be running two of these processes concurrently under a single shell. If that were the case, we would implement some sort of worker pool. The upper bound of process identifiers is considered acceptable with regard to the fact that every node id is stored for the lifetime of an Erlang instance in the atoms table. We can tollerate having a hundred thousand identifiers in there if need be, we cannot tollerate millions of identifiers.
$$
is defined to be the shell pid under sh/bash (tested upon both)
echo "$$-$(date +%s)"
❯ 47274-1526831392
CLIENT_NODE_ID="$$-$(date +%s)"
Does that look like a good node id generator, I think not, someone could write a script to poll their Riak cluster every second, it wouldn't be long before the atom table would get filled up.
So, the node identifier will look like this
echo "$$"
❯ 70636
CLIENT_NODE_ID="$$"
Lets try that ...
CLIENT_NODE_ID="$$"
IFS='' read -r -d '' ERL_CMD <<'EOF'
Res = net:ping('dev1@127.0.0.1'),
io:put_chars(standard_error,io_lib:format("~p",[Res])).
EOF
erl -setcookie riak -name "${CLIENT_NODE_ID}-@127.0.0.1" -noshell \
-eval $ERL_CMD -eval "erlang:halt()."
It would be nice to extract some stuff into variables, those variables can become parameters when we turn this into a shell script.
CLIENT_HOST="127.0.0.1"
CLIENT_NODE_ID="$$"
TARGET_HOST="dev1@127.0.0.1"
IFS='' read -r -d '' ERL_CMD <<EOF
Res = net:ping('${TARGET_HOST}'),
io:put_chars(standard_error,io_lib:format("~p",[Res])).
EOF
erl -setcookie riak \
-name "${CLIENT_NODE_ID}@${CLIENT_HOST}" \
-noshell \
-eval $ERL_CMD \
-eval "erlang:halt()."
Now we can supply templated Erlang - if need be, we can adapt the system later to store those values outside of the script
I found this nugget on stack overflow - "Forcing bash to expand variables in a string loaded from a file" (user : wjl)
apply_shell_expansion() { declare file="$1" declare data=$(< "$file") declare delimiter="__apply_shell_expansion_delimiter__" declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter" eval "$command" }
For example, you can use it like this with a parameters.cfg which is really a shell script that just sets variables, and a >>>template.txt which is a template that uses those variables:
. parameters.cfg printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt
❯ cat ./cmd.sh
#!/bin/sh
CLIENT_HOST="127.0.0.1"
CLIENT_NODE_ID="$$-freeak-client"
TARGET_HOST="dev1@127.0.0.1"
apply_shell_expansion() {
declare file="$1"
declare data=$(< "$file")
declare delimiter="__apply_shell_expansion_delimiter__"
declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
eval "$command"
}
erl -setcookie riak \
-name "${CLIENT_NODE_ID}@${CLIENT_HOST}" \
-noshell \
-eval "$(apply_shell_expansion ./erlang-commands.template)" \
-eval "erlang:halt()."
❯ cat ./erlang-commands.template
Res = net:ping('${TARGET_HOST}'),
io:put_chars(standard_error,io_lib:format("~p",[Res])).
Let's try it out :
~ master*
❯ chmod 755 ./cmd.sh
~ master*
❯ ./cmd.sh
pong
I like this! Very tidy !
Let's take a break!