Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bryanhuntesl/b954e8bb3b7550b3a950a3e31567728b to your computer and use it in GitHub Desktop.
Save bryanhuntesl/b954e8bb3b7550b3a950a3e31567728b to your computer and use it in GitHub Desktop.
Riak and Erlang - execute a command upon a node and print to console

verify distributed Erlang working

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.

Improve the structuring of the code execution command

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()."

What to do about node identifier id's ?

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()."

improve again

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

creating a templated Erlang reader

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment