Skip to main content

Testing erlang ldap lib eldap with local ldap server the easy way

First we need to pick an ldap server to run, I've heard horror stories about setting up ldap servers locally for testing purposes so I was surpised by how easy it was to setup a ldap server using Apache Directory Studio which is a kind of Visual Tool to manage Apache Directory from an Eclipse based UI.

We start by downloading it from the Apache Directory Download Page

After downloading and unpacking it (snippet for the lazy linux users):

wget http://mirrors.fe.up.pt/pub/apache//directory/studio/dist/2.0.0.v20130628/ApacheDirectoryStudio-linux-x86_64-2.0.0.v20130628.tar.gz

tar -xzf ApacheDirectoryStudio-linux-x86_64-2.0.0.v20130628.tar.gz

Now we start it, in my case I had some segfaults as noted on the download page so I had to add the variable declaration before, it works for fedora, you may have to change the path a little:

GTK2_RC_FILES=/usr/share/themes/Raleigh/gtk-2.0/gtkrc ./ApacheDirectoryStudio

Now that we have it running we will see something like this:

../galleries/code/ldap-erlang/1-first-session.png

We first create a new server by clicking on new server on the bottom left:

../galleries/code/ldap-erlang/2-new-server.png

We pick "ApacheDS 2.0.0" just because we like to be in the bleeding edge (?):

../galleries/code/ldap-erlang/3-select-version.png

If we double click the created server we can see the config, I leave everything as default:

../galleries/code/ldap-erlang/4-server-config.png

We now start the server by clicking the green run button on the bottom left:

../galleries/code/ldap-erlang/5-server-run.png

Then we want to create a connection, we do that by right clicking the server and selecting "Create a Connection":

../galleries/code/ldap-erlang/6-create-connection.png

Then we connect:

../galleries/code/ldap-erlang/7-open-connection.png

Now we can see the details of the server on the top left panel, we click the entry named "dc=example,dc=com" (in my case, if you changed the config it may be different for you.

on the pop up menu we pick "New -> New Entry":

../galleries/code/ldap-erlang/8-new-dc-entry.png

On the dialog we select "Create entry from scratch" and click next:

../galleries/code/ldap-erlang/9-from-scratch.png

We want to create an organization unit to hold our users so we start typing "org" on the "Available object classes" entry until we see the entry "organizationalUnit":

../galleries/code/ldap-erlang/10-organization-unit.png

We select it and click the "Add" button, when the right panel is populated we click "Next >"

../galleries/code/ldap-erlang/11-org-unit-add-next.png

On the next step of the dialog we enter "ou" on the RDN entry and "users" on the right side and click "Next >"

../galleries/code/ldap-erlang/12-ou-users.png

On the next step we click "Finish"

../galleries/code/ldap-erlang/13-users-finish.png

Now we want to create a new user under our "users" organization unit, to do that we right click ont he users ou and again select "New -> New Entry" and pick "Create entry from scratch" and then "Next >".

../galleries/code/ldap-erlang/14-create-inet-org-person.png

after that we look for the object class "inetOrgPerson" and click "Add" and then "Next >":

../galleries/code/ldap-erlang/15-add-next.png

On the next step we enter uid as RDN and the username as value on the right side, in my case it will be mariano because I'm egocentric :P

../galleries/code/ldap-erlang/16-user-uid.png

Then we click "Next >" and on the next step we fill cn (Common Name) and sn (Surename) and click "Finish"

by the way, dc is Domain Component and dn is Distinguished Name ;)

../galleries/code/ldap-erlang/17-set-cn-sn.png

Now we have our first user but it doesn't have a password, let's set it by double clicking the user on the top left pannel and clicking the "New Attribute" on the bar at the top of the center panel:

../galleries/code/ldap-erlang/18-user-new-attr-user-password.png

On the dialog that opens we pick "Attribute type" "userPassword" and click "Next >"

../galleries/code/ldap-erlang/19-select-user-password-next.png

We enter a password:

../galleries/code/ldap-erlang/20-set-password-ok.png

And that's it, we have a user with a password inside an organization unit!

../galleries/code/ldap-erlang/21-user-result.png

Now that we have the server running and one user we can code some erlang to try it.

Most of the code was taken from the Erlang Central Article "How To Talk LDAP from Erlang"

1> Host = "127.0.0.1".
"127.0.0.1"
2> Port = 10389.
10389
3> {_,S} = eldap:open([Host], [{port, Port}]).
{ok,<0.37.0>}
4> UserRest = "ou=users,dc=example,dc=com".
"ou=users,dc=example,dc=com"
5> Username = "mariano".
"mariano"
6> Password = "secret".
"secret"
7> DN = "uid=" ++ Username ++ "," ++ UserRest.
"uid=mariano,ou=users,dc=example,dc=com"
8>
8> eldap:simple_bind(S, DN, Password).
ok
9> eldap:simple_bind(S, DN, "anothersecret").
{error,invalidCredentials}

and that's the basics, there's some more code in the article were this code was based.

You can also find the docs for the eldap library in the erlang documentation for eldap

basic TCP echo server with rebar, reltool, ranch and lager

create project skeleton:

mkdir eco
cd eco
wget https://github.com/rebar/rebar/wiki/rebar
chmod u+x rebar
./rebar create-app appid=eco

let's add some dependencies, ranch to accept tcp connections and lager for logging, for that open rebar.config with your text editor and enter this:

{deps, [
    {lager, "2.1.0", {git, "https://github.com/basho/lager", {tag, "2.1.0"}}},
    {ranch, "1.1.0", {git, "https://github.com/ninenines/ranch", {tag, "1.1.0"}}}
]}.

{erl_opts, [debug_info, {parse_transform, lager_transform}]}.

Note

if you put lager dep after ranch you will get an error when compiling, that's sad

now let's try compiling it:

./rebar get-deps
./rebar compile

we can start our app from the shell, which won't be really useful, but why not:

erl -pa ebin/ deps/*/ebin

and we run:

1> application:start(eco).
ok

now let's use ranch and lager for something, first we create a protocol implementation, open a file called eco_protocol.erl and put the following content in it:

-module(eco_protocol).
-behaviour(ranch_protocol).

-export([start_link/4]).
-export([init/4]).

start_link(Ref, Socket, Transport, Opts) ->
    Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
    {ok, Pid}.

init(Ref, Socket, Transport, _Opts = []) ->
    ok = ranch:accept_ack(Ref),
    loop(Socket, Transport).

loop(Socket, Transport) ->
    case Transport:recv(Socket, 0, 5000) of
        {ok, Data} ->
            lager:info("echoing ~p", [Data]),
            Transport:send(Socket, Data),
            loop(Socket, Transport);
        _ ->
            ok = Transport:close(Socket)
    end.

edit the start function in src/eco_app.erl so it looks like this:

start(_StartType, _StartArgs) ->
    {ok, _} = ranch:start_listener(eco, 1, ranch_tcp, [{port, 1883}],
                                                        eco_protocol, []),
    eco_sup:start_link().

and add the apps we need in eco.app.src by adding ranch and lager to the applications entry like this:

{applications, [
                kernel,
                stdlib,
                ranch,
                lager
               ]},

now let's compile and try again:

./rebar compile
Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V6.3  (abort with ^G)
1> application:start(eco).
{error,{not_started,ranch}}
2> application:start(ranch).
ok
3> application:start(eco).
{error,{not_started,lager}}
4> application:start(lager).
{error,{not_started,goldrush}}
5> application:start(goldrush).
{error,{not_started,syntax_tools}}
6> application:start(syntax_tools).
ok
7> application:start(goldrush).
{error,{not_started,compiler}}
8> application:start(compiler).
ok
9> application:start(goldrush).
ok
10> application:start(lager).
ok
11> 21:05:52.373 [info] Application lager started on node nonode@nohost
11> application:start(eco).
ok
21:06:09.335 [info] Application eco started on node nonode@nohost

Note

user Cloven from reddit noted that instead of starting all the applications by hand in order you could use:

application:ensure_all_started(eco).

I was sure there was a way to do it since each app specified the dependencies, you can tell from the fact that each app tells you which one it needs before starting, but I didn't know which was the function to call.

thanks to him!

now let's send some data:

telnet localhost 1883

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
asd
asd

(I wrote the first asd, the second is the reply)

in the console you should see this log line:

21:10:05.098 [info] echoing <<"asd\r\n">>

now let's build a release so others can use our server (?):

mkdir rel
cd rel
../rebar create-node nodeid=eco

add the following two lines to rebar.config:

{sub_dirs, ["rel"]}.
{lib_dirs, ["deps"]}.

and edit rel/reltool.config, change the lib_dirs entry to this:

{lib_dirs, ["../deps"]},

add ranch and lager in the rel entry:

{rel, "eco", "1",
 [
  kernel,
  stdlib,
  sasl,
  ranch,
  lager,
  eco
 ]},

and change the app, echo entry to look like this:

{app, eco, [{mod_cond, app}, {incl_cond, include}, {lib_dir, ".."}]}

now let's try to build a release:

./rebar compile
./rebar generate

now let's start our server:

./rel/eco/bin/eco console

you should see some output like this:

Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]


=INFO REPORT==== 5-Feb-2015::22:15:22 ===
inet_parse:"/etc/resolv.conf":4: erroneous line, SKIPPED
21:15:22.393 [info] Application lager started on node 'eco@127.0.0.1'
21:15:22.394 [info] Application eco started on node 'eco@127.0.0.1'
Eshell V6.3  (abort with ^G)
(eco@127.0.0.1)1>

now let's telnet again:

telnet localhost 1883

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
lala!
lala!

on the console again you should see some log like this:

21:16:01.540 [info] echoing <<"lala!\r\n">>

and that's it, now evolve your echo server into an actual server :)

How To Setup Vagrant Android/phonegap Build Env

this is a brain dump of something I did and I want documented somewhere.

first install vagrant, I won't go over it.

then do:

vagrant init

then edit the generated Vagrant file, I changed this:

# use ubuntu trusty
config.vm.box = "ubuntu/trusty64"


# 1GB or ram
config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
end

# provision all the needed packages, I'm using it to build a phonegap app
# that's why I install all the npm stuff
config.vm.provision "shell", inline: <<-SHELL
  sudo apt-get update
  sudo apt-get install -y ant lib32ncurses5 lib32stdc++6 lib32z1 npm build-essential git nodejs-legacy openjdk-7-jdk
  sudo npm install -g "phonegap@3.5.0-0.21.14" bower grunt-cli
SHELL

then:

vagrant up
vagrant ssh

inside the vm:

cd
wget http://dl.google.com/android/android-sdk_r24.0.2-linux.tgz
tar -xzf android-sdk_r24.0.2-linux.tgz

echo "export PATH=\$PATH:\$HOME/android-sdk-linux/tools:\$HOME/android-sdk-linux/platform-tools:\$HOME/android-sdk-linux/build-tools/19.1.0" >> $HOME/.bashrc
bash

android update sdk -a --no-ui --filter "platform-tools"
android update sdk -a --no-ui --filter "android-19"
android update sdk -a --no-ui --filter "sys-img-armeabi-v7a-android-19"
android update sdk -a --no-ui --filter "build-tools-19.1.0"
android create avd --name myandroid -t "android-19"

I installed the version 19 of the sdk because of project requirements, feel free to install something more up to date, to see what you can install you can run:

android list sdk --extended

Youtube Video to Audio Download

this is a reminder for myself in the future, I tend to watch/listen to talks when I'm doing something like cooking, cleaning.

but when I'm outside I want to take my music player and the talks tend to be on youtube, so normally I search for "youtube to mp3" and there's some ad ridden site that does that (sometimes)

but today I wanted to learn how to do it from the cli and here is the post that explains it so I can come back int he future to copy paste the commands.

first install youtube-dl and ffmpeg:

sudo yum install youtube-dl ffmpeg

then download your video:

youtube-dl https://www.youtube.com/watch\?v\=06h21nBqwec

then extract the audio from the video:

ffmpeg -i Music\ Theory\ and\ Performance\ Analysis\ with\ Sebastian\ and\ Czerny-06h21nBqwec.webm -strict experimental -acodec vorbis -ab 128k -vn ./output.ogg

then do whatever you want with the video and the audio

Overtone, clojure, jackd, alsa in ubuntu 14.10

ok, this is a small dump of what I did, I'm surely missing some stuff and surely it won't work as is for you, but maybe it does and you are as happy as I'm right now generating noise (not as happy as anyone close to me at this moment).

all the things I think I installed:

sudo apt install alsa-tools alsaplayer-jack alsa-utils pulseaudio-module-jack supercollider jack-tools fftw3 qjackctl openjdk-8-jdk

yes, you can see the desperation there with some stuff that may not make sense, but at least it works..

you need to install leiningen, it's really easy and it has good instructions on the site so I won't go into details, just follow the instructions here: http://leiningen.org/

then I follow the instructions from overtone's wiki here:

https://github.com/overtone/overtone/wiki/Installing-overtone

before starting our repl and rocking like a hurricane we need to kill pulseaudio and start jack, it sounds easier than it is because pulseaudio just won't stay dead :(

the way I found to make it work was to edit pulseadio client.conf:

sudo vim /etc/pulse/client.conf

uncomment the line (remove the semicolon):

; autospawn = yes

and leaving it like this:

autospawn = no

I added myself to the audio group, not sure if it's required but just in case:

sudo adduser $USER audio

for this to take effect you need to logout and login again, to make sure you have the group, open a terminal and run:

groups

you should see audio between some other groups, if you can't see it try rebooting or replacing $USER with your actual username in the adduser command.

now stop pulseaudio:

pulseaudio --kill

then start jackd, I tried all the combinations I could find on the internet without success, this is the one that worked for me:

jackd -R -d alsa -d hw:1

if that doesn't work try:

jackd -R -d alsa

or try the versions that are recommended on the overtone wiki:

jackd -r -d alsa -r 44100

or:

jackd -r -d alsa -r 44100 -P

you can also try running qjackctl and play with the settings to see if you have luck.

now that we (hopefully) have jackd running, we can start playing with overtone, here is a small dump of a session:

lein new tutorial
cd tutorial

# add [overtone "0.9.1"] to :dependencies
vim project.clj

lein deps
lein repl

# inside the clojure repl
user=> (use 'overtone.live)
user=> (definst foo [] (saw 220))
#<instrument: foo>
user=> (foo)
#<synth-node[loading]: user/foo 35>
user=> (kill 35)

when you are done you can stop jackd from the ui or from the shell however you started it and start again pulseaudio:

pulseaudio --start

some additional note, when I'm using jackd I can't set the volume from the media keys or the sound mixer in the top planel, I run:

alsamixer

press F6, select the output I'm using and change it with the keys (ESC to close).

hope it's useful for someone.

Making a Chat App with Erlang, Rebar, Cowboy and Bullet

this is a continuation of the post about erlang, cowboy and rebar.

let' start by adding the bullet dep:

{sub_dirs, ["rel"]}.

{deps, [
    {cowboy, "1.0.0", {git, "https://github.com/ninenines/cowboy", {tag, "1.0.0"}}},
    {bullet, "0.4.1", {git, "https://github.com/extend/bullet", {tag, "0.4.1"}}}
]}.

now get the deps:

./rebar get-deps

add bullet as a dependency on disrupt.app.src:

{application, disrupt,
 [
  {description, ""},
  {vsn, "1"},
  {registered, []},
  {applications, [
                  kernel,
                  stdlib,
                  cowboy,
                  bullet
                 ]},
  {mod, { disrupt_app, []}},
  {env, []}
 ]}.

register our bullet handler with cowboy:

start(_StartType, _StartArgs) ->
    {ok, ChannelPid} = disrupt_channel:new(),
    Dispatch = cowboy_router:compile([
        {'_', [
               {"/chat", bullet_handler, [{handler, disrupt_chat_handler}, {channel, ChannelPid}]},
               {"/ui/[...]", cowboy_static, {priv_dir, disrupt, "assets",
                                             [{mimetypes, cow_mimetypes, all}]}}
        ]}
    ]),
    {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
        {env, [{dispatch, Dispatch}]}
    ]),
    disrupt_sup:start_link().

the important lines are:

{ok, ChannelPid} = disrupt_channel:new(),

were we create a new chat channel that will be passed to all disrupt_chat_handler instances here:

{"/chat", bullet_handler, [{handler, disrupt_chat_handler}, {channel, ChannelPid}]},

also note that I changed the path of the ui to /ui/... instead of it being at the root.

add the js libs we need to use bullet (yes, I could use bower, but let's keep it simple):

mkdir priv/assets/vendor
mkdir priv/assets/js
cp deps/bullet/priv/bullet.js priv/assets/vendor
wget http://code.jquery.com/jquery-2.1.1.min.js -O priv/assets/vendor/jquery.js

we create a really simple chat page at index.html:

<!doctype html>
<html>
 <head>
  <title>Chat!</title>
  <meta charset="utf-8">
  <script src="vendor/jquery.js"></script>
  <script src="vendor/bullet.js"></script>
  <script src="js/app.js"></script>
  <style>
   body{ font-family: helvetica; color: #333; background-color: #fefefe;
    margin-left: 25%; width: 50%; }
   p, textarea{ padding: 0; }
   p, button, textarea{ width: 100%; margin: 1em 0; }
   label{ float: left; width: 45%; }
   input{ width: 50%; float: right; }
   input, textarea{ border: 1px solid #ddd; }
  </style>
 </head>
 <body>
  <p>
  <label for="input">Nickname</label>
  <input type="text" id="nick" value="anonymous"/>
  </p>
  <textarea id="output" cols="80" rows="25"></textarea>
  <p>
  <label for="input">Input</label>
  <input type="text" id="input"/>
  <button id="send">Send</button>
  </p>
 </body>
</html>

add code to handle the chat app on priv/assets/js/app.js:

/*globals $, document, window*/
function disruptApp(document, window, $) {
    'use strict';
    var input = document.getElementById('input'),
        output = document.getElementById('output'),
        nickInput = document.getElementById('nick'),
        send = document.getElementById('send'),

        connection;

    function sendMessage(text) {
        var nick = getNick();
        connection.send(nick + ': ' + text);
    }

    function onSendClicked() {
        var text = input.value.trim();

        if (text !== '') {
            sendMessage(text);
        }

        input.value = '';
    }

    function getNick() {
        var nick = nickInput.value.trim();

        if (nick === '') {
            return 'anonymous';
        } else {
            return nick;
        }
    }

    function notify(text) {
        var date = (new Date()).toLocaleString();
        output.innerHTML = output.innerHTML + '[' + date + '] ' + text + '\n';
    }

    function onData(data) {
        notify(data);
    }

    send.addEventListener('click', onSendClicked);

    function start(url, options, notify, onData) {
        var connection = $.bullet(url, options);

        connection.onopen = function(){
            notify('online');
        };

        connection.onclose = connection.ondisconnect = function(){
            notify('offline');
        };

        connection.onmessage = function(e){
            if (e.data === 'pong'){
                notify('pong');
            } else {
                onData(e.data);
            }
        };

        connection.onheartbeat = function(){
            connection.send('ping');
            notify('ping');
        };

        return connection;
    }

    connection = start('ws://localhost:8080/chat', {}, notify, onData);
}

document.addEventListener("DOMContentLoaded", function() {
    'use strict';
    disruptApp(document, window, $);
});

we need a simple pubsub mechanism for channels, I won't explain it here in detail but you can read about erlang's gen_event behaviour which is the one that does all the work:

-module(disrupt_channel).
-behaviour(gen_event).

-export([new/0, subscribe/2, unsubscribe/2, send/2]).

-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
         terminate/2]).
%% API

new() -> gen_event:start_link().

subscribe(Channel, Pid) ->
    gen_event:add_handler(Channel, {disrupt_channel, Pid}, [Pid]).

unsubscribe(Channel, Pid) ->
    gen_event:delete_handler(Channel, {disrupt_channel, Pid}, [Pid]).

send(Channel, Event) ->
    gen_event:notify(Channel, Event).

-record(state, {pid}).
%% callbacks
init([Pid]) -> {ok, #state{pid=Pid}}.

handle_event(Msg, State=#state{pid=Pid}) ->
    Pid ! Msg,
    {ok, State}.

handle_call(_, State) -> {ok, ok, State}.

handle_info(_, State) -> {ok, State}.

code_change(_OldVsn, State, _Extra) -> {ok, State}.

terminate(_Reason, _State) -> ok.

the important part from the code above is the fact that we store the pid of the bullet handler on the gen_event instance so we can send the message back when we get notified:

handle_event(Msg, State=#state{pid=Pid}) ->
    Pid ! Msg,
    {ok, State}.

the bullet handler for the chat channel is:

-module(disrupt_chat_handler).

-export([init/4, stream/3, info/3, terminate/2]).

-record(state, {channel}).

init(_Transport, Req, Opts, _Active) ->
    io:format("channel init ~p~n", [Opts]),
    {channel, ChannelPid} = lists:keyfind(channel, 1, Opts),
    disrupt_channel:subscribe(ChannelPid, self()),
    {ok, Req, #state{channel=ChannelPid}}.

stream(<<"ping">>, Req, State) ->
    io:format("ping received~n"),
    {reply, <<"pong">>, Req, State};

stream(Data, Req, State=#state{channel=ChannelPid}) ->
    io:format("message received ~s~n", [Data]),
    disrupt_channel:send(ChannelPid, {msg, self(), Data}),
    {ok, Req, State}.

info({msg, _Sender, Data}, Req, State) ->
    io:format("msg received ~p~n", [Data]),
    {reply, Data, Req, State}.

terminate(_Req, #state{channel=ChannelPid}) ->
    io:format("unsubscribing from channel~n"),
    disrupt_channel:unsubscribe(ChannelPid, self()),
    ok.

we subscribe on init, unsubscribe on terminate, when we receive a ping message we reply it only to the sender with pong, if we receive something else we send the message to the channel so it gets sent to all subscribers, that get the message on the info function where they send it to the browsers.

build:

rm -rf rel/disrupt && ./rebar compile generate

run:

./rel/disrupt/bin/disrupt console

open http://localhost:8080/ui/index.html in two or more browsers and chat!

../galleries/misc/bullet-chat.png

Serving Static Files with Erlang, Cowboy and Rebar, (raw material)

the "serving static files from a server" market needs some disruption, let's tackle that problem with some erlang.

create the folder:

mkdir disrupt
cd disrupt

get rebar:

wget http://cloud.github.com/downloads/basho/rebar/rebar
chmod u+x rebar

generate app:

./rebar create-app appid=disrupt

add dependencies:

vim rebar.config

the file should contain the following code:

{deps, [
    {cowboy, "1.0.0", {git, "https://github.com/ninenines/cowboy", {tag, "1.0.0"}}}
]}.

this tells to get cowboy 1.0.0 from git as dependency, let's fetch the dependencies:

./rebar get-deps

now let's serve some static files, open src/disrupt_app.erl, change the start function so it looks like this:

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        {'_', [
            {"/[...]", cowboy_static, {priv_dir, disrupt, "assets",
                [{mimetypes, cow_mimetypes, all}]}}
        ]}
    ]),
    {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
        {env, [{dispatch, Dispatch}]}
    ]),
    disrupt_sup:start_link().

the code above is taken from the static example from cowboy adapted to our needs: https://github.com/ninenines/cowboy/tree/master/examples/static_world

docs here: http://ninenines.eu/docs/en/cowboy/1.0/guide/static_handlers/

now make the priv/assets folder:

mkdir -p priv/assets

and put some content in the index file:

echo "hello static world" > priv/assets/index.html

let's make a release for the project, first create the release files:

mkdir rel
cd rel
../rebar create-node nodeid=disrupt
cd ..

edit rel/reltool.config, the line:

{lib_dirs, []},

should change to:

{lib_dirs, ["../deps"]},

add the line:

sub_dirs, ["rel"]}.

to the top of the rebar.config file

if not you get this error:

Command 'generate' not understood or not applicable

yay helpfulness!

now we are one confusing error closer to our goal, but now we get:

ERROR: generate failed while processing /home/mariano/tmp/disrupt/rel: {'EXIT',{{badmatch,{error,"disrupt: Missing application directory."}},
         [{rebar_reltool,generate,2,[]},
          {rebar_core,run_modules,4,[]},
          {rebar_core,execute,5,[]},
          {rebar_core,process_dir0,6,[]},
          {rebar_core,process_dir,4,[]},
          {rebar_core,process_each,5,[]},
          {rebar_core,process_dir0,6,[]},
          {rebar_core,process_dir,4,[]}]}}

only because I've seen some other erlang projects I decided that this may be the solution:

mkdir -p apps/disrupt
cd apps/disrupt
ln -s ../../src
ln -s ../../ebin
ln -s ../../priv
cd ../..

we try again to make the release and we get the error again, nice!

for no apparent reason that the fact that I saw it on some other project, change the line on rel/reltool.config:

{lib_dirs, ["../deps"]},

to:

{lib_dirs, ["../deps", "../apps"]},

trying again:

./rebar compile generate

success!

let's try running it:

./rel/disrupt/bin/disrupt console

and what do we get? well, a crash! \o/:

Exec: /home/mariano/tmp/disrupt/rel/disrupt/erts-5.10.4/bin/erlexec -boot /home/mariano/tmp/disrupt/rel/disrupt/releases/1/disrupt -mode embedded -config /home/mariano/tmp/disrupt/rel/disrupt/releases/1/sys.config -args_file /home/mariano/tmp/disrupt/rel/disrupt/releases/1/vm.args -- console
Root: /home/mariano/tmp/disrupt/rel/disrupt
Erlang R16B03 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
(disrupt@127.0.0.1)1>
=INFO REPORT==== 29-Aug-2014::11:49:32 ===
    application: disrupt
    exited: {bad_return,
             {{disrupt_app,start,[normal,[]]},
              {'EXIT',
               {undef,
                [{cowboy_router,compile,
                  [[{'_',
                     [{"/[...]",cowboy_static,
                       {priv_dir,disrupt,"assets",
                        [{mimetypes,cow_mimetypes,all}]}}]}]],
                  []},
                 {disrupt_app,start,2,
                  [{file,"src/disrupt_app.erl"},{line,13}]},
                 {application_master,start_it_old,4,
                  [{file,"application_master.erl"},{line,269}]}]}}}}
    type: permanent
{"Kernel pid terminated",application_controller,"{application_start_failure,disrupt,{bad_return,{{disrupt_app,start,[normal,[]]},{'EXIT',{undef,[{cowboy_router,compile,[[{'_',[{\"/[...]\",cowboy_static,{priv_dir,disrupt,\"assets\",[{mimetypes,cow_mimetypes,all}]}}]}]],[]},{disrupt_app,start,2,[{file,\"src/disrupt_app.erl\"},{line,13}]},{application_master,start_it_old,4,[{file,\"application_master.erl\"},{line,269}]}]}}}}}"}

Crash dump was written to: erl_crash.dump
Kernel pid terminated (application_controller) ({application_start_failure,disrupt,{bad_return,{{disrupt_app,start,[normal,[]]},{'EXIT',{undef,[{cowboy_router,compile,[[{'_',[{"/[...]",cowboy_static

with all my time reading erlang crashes I see an undef there, it seems the app cant find cowboy_router:compile

but I have it on my deps..

well, let's put it on src/disrupt.app.src applications for no other reason that I've seen people do that, the file should look like this:

{application, disrupt,
 [
  {description, ""},
  {vsn, "1"},
  {registered, []},
  {applications, [
                  kernel,
                  stdlib,
                  cowboy
                 ]},
  {mod, { disrupt_app, []}},
  {env, []}
 ]}.

see the cowboy as last element on the applications list? that's what you should add.

let's try again:

./rebar compile generate

and I get:

<some output removed here>

==> rel (generate)
ERROR: generate failed while processing /home/mariano/tmp/disrupt/rel: {'EXIT',{{badmatch,{error,"Application cowboy is used in release \"disrupt\" and cannot be excluded"}},
         [{rebar_reltool,generate,2,[]},
          {rebar_core,run_modules,4,[]},
          {rebar_core,execute,5,[]},
          {rebar_core,process_dir0,6,[]},
          {rebar_core,process_dir,4,[]},
          {rebar_core,process_each,5,[]},
          {rebar_core,process_dir0,6,[]},
          {rebar_core,process_dir,4,[]}]}}

"Application cowboy is used in release "disrupt" and cannot be excluded"

who told you to exclude it?

let's apply a technic we already used a lot before, let's make up reasons for what it may be failing, it says exclude there and I've seen a lot of exclude and include atoms in rel/reltool.config, maybe it's that?

let's layer some other technic I use a lot, let's try to make that file look as similar as another one I've seen that works, in this case it's the reltool.config file generated by riak_core rebar template:

https://github.com/basho/rebar_riak_core/blob/master/riak_core.reltool.config

ok, that one seems to have less stuff than ours, let's start commenting everything that looks different until we reach this point:

{sys, [
    {lib_dirs, ["../deps", "../apps"]},
        %{erts, [{mod_cond, derived}, {app_file, strip}]},
        %{app_file, strip},
        {rel, "disrupt", "1",
            [
                kernel,
            stdlib,
            sasl,
            disrupt
                ]},
        {rel, "start_clean", "",
            [
                kernel,
            stdlib
                ]},
        {boot_rel, "disrupt"},
        {profile, embedded},
        %{incl_cond, exclude},
        {excl_archive_filters, [".*"]}, %% Do not archive built libs
    {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)",
        "^erts.*/(doc|info|include|lib|man|src)"]},
        {excl_app_filters, ["\.gitignore"]},
        {app, sasl,   [{incl_cond, include}]},
        %{app, stdlib, [{incl_cond, include}]},
        %{app, kernel, [{incl_cond, include}]},
        {app, disrupt, [{incl_cond, include}]}
    ]}.

{target_dir, "disrupt"}.

{overlay, [
    {mkdir, "log/sasl"},
        {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
        {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
        {copy, "files/disrupt", "bin/disrupt"},
        {copy, "files/disrupt.cmd", "bin/disrupt.cmd"},
        {copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
        {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
        {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
        {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
    ]}.

trying again:

rm -rf rel/disrupt
./rebar compile generate

success!

now let's try running it:

./rel/disrupt/bin/disrupt console

the output we get:

Exec: /home/mariano/tmp/disrupt/rel/disrupt/erts-5.10.4/bin/erlexec -boot /home/mariano/tmp/disrupt/rel/disrupt/releases/1/disrupt -mode embedded -config /home/mariano/tmp/disrupt/rel/disrupt/releases/1/sys.config -args_file /home/mariano/tmp/disrupt/rel/disrupt/releases/1/vm.args -- console
Root: /home/mariano/tmp/disrupt/rel/disrupt
Erlang R16B03 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
(disrupt@127.0.0.1)1>

that's what I would call a non crashing system, let's see if it works:

curl http://localhost:8080/index.html

we get:

hello static world

success!

and that's how you serve a static file with erlang, cowboy and rebar :)

How To Build Twister Distributed Microblog on Ubuntu 13.10 (Saucy)

today I found out about twisterd and I wanted to give it a try (and reserve my username ;) so I tried and it took a while to get all the dependencies right, to avoid you the pain here is the guide.

take into account the comments in the script.

just a console dump, it should work just by pasting the commands in order:

mkdir twister
cd twister
sudo apt-get update
sudo apt-get install libssl-dev libdb-dev libdb++-dev libminiupnpc-dev libboost-all-dev build-essential git autoconf libtool
git clone https://github.com/miguelfreitas/twister-core.git
git clone https://github.com/miguelfreitas/twister-html.git
mkdir ~/.twister
mv twister-html ~/.twister/html
cd twister-core/libtorrent

# NOTE: the following command will fail with an error about boost, ignore
# it and run the following commands

./bootstrap.sh
./configure --enable-logging --enable-debug --enable-dht --with-boost-libdir=/usr/lib/x86_64-linux-gnu/
make
cd ../src
make -f makefile.unix
./twisterd -daemon -rpcuser=user -rpcpassword=pwd -rpcallowip=127.0.0.1

BTW I'm @mariano :)

Install Immutant 1.0 full as a service on debian/ubuntu

Small dump with instructions to install immutant as a service on debian, ubuntu or derivatives.

I adapted the init script that comes with immutant to work on debian:

# install needed packages
sudo apt-get install wget unzip openjdk-7-jdk daemon

# create a dir to download files
mkdir ~/soft
cd ~/soft

# download immutant 1.0 full
wget http://repository-projectodd.forge.cloudbees.com/release/org/immutant/immutant-dist/1.0.0/immutant-dist-1.0.0-full.zip

unzip immutant-dist-1.0.0-full.zip

# create user that will run the service
sudo adduser jboss-as

# create required folders
sudo cp -r immutant-1.0.0-full/jboss /usr/share/jboss-as/
sudo mkdir -p /var/log/jboss-as /var/run/jboss-as/

# set permissions
sudo chown -R jboss-as.jboss-as /var/log/jboss-as /var/run/jboss-as /usr/share/jboss-as/

# get and install init.d script
sudo wget https://gist.github.com/marianoguerra/6211268/raw/6e248a66670f0ef247d9aa169873c011b275da86/jboss-as-standalone.sh -O /etc/init.d/jboss-as-standalone
sudo chmod u+x /etc/init.d/jboss-as-standalone

# register the service
update-rc.d jboss-as-standalone default

# start it
sudo /etc/init.d/jboss-as-standalone start

Viaje de navidad - Alghero

Cuando contamos que nos íbamos a Italia alguien dijo que en Serdegna estaba bueno Alghero.

../galleries/Alghero/IMG_5638.JPG

FreeBSD.it

Resultó que Alghero queda en la otra punta de la isla y que la isla no es algo así como pequeña.

../galleries/Alghero/IMG_5641.JPG

Ida en colectivo el 24 de Diciembre, vuelta en tren el 26 (feriado).

La ciudad muy linda, la parte que da al mar esta muy buena.

../galleries/Alghero/IMG_5645.JPG

Ahí el 25 como no teníamos nada que hacer decidimos caminar hasta las grutas de Neptuno, pero dado que era feriado no había ni medio de transporte y estábamos casi seguros que estaba cerrada.

../galleries/Alghero/IMG_5652.JPG

Luego de 4 horas de caminar una media maratón (21 Km) por la ruta llegamos para enfrentarnos con la esperada realidad, las grutas estaban cerradas.

Durante la ida habíamos decidido, dado nuestro estado, intentar hacer dedo a la vuelta.

../galleries/Alghero/IMG_5667.JPG

Primer intento... exitoso! nos volvimos con un italiano que vivía en una ciudad cercana hablando mi cocoliche aprendido días antes de un libro germanamente titulado "Italienish für Dummies".

En un principio nos iba a llevar hasta un pueblo cercano ya que le quedaba a contramano, pero se copó y nos llevo hasta la entrada de Alghero.

../galleries/Alghero/IMG_5653.JPG

A la vuelta a las 6 de la mañana en la estación de trenes donde no había trenes (por el feriado) y donde el reloj de la estación no andaba (?) esperamos el colectivo con dos norteamericanas, que luego volvimos a encontrar en la estación del siguiente pueblo, que luego volvimos a encontrar en el tren a Cagliari, que luego volvimos a encontrar en la fila para abordar el vuelo...

../galleries/Alghero/IMG_5654.JPG

En el avión les pregunto si por casualidad iban a Stuttgart así compartíamos ticket de tren, pero ya como lo dijo Heisenberg, al intentar intervenir en este proceso de coincidencias las interrumpí, iban a Freiburg.

../galleries/Alghero/IMG_5656.JPG

Llegados al aeropuerto fui identificado como un potencial terrorista* y me solicitaron amablemente vaya a una fila donde todos los seres "aleatoriamente" seleccionados eran idénticos a mi (no solo notado por mi sino por el primero en la fila quien dijo "somos todos iguales!") excepto una blonda alemana que fue introducida en el set para romper el patrón** intentando así evitar que nuestras mentes inferiores identificaran el patrón.

../galleries/Alghero/IMG_5657.JPG

Luego del chequeo de pasaporte nos damos con que habíamos perdido el ultimo colectivo a la ciudad mas cercana con estación de tren, por suerte unos amigos (Lucas y Liza) nos habían ido a buscar de semi sorpresa para invitarnos a cenar.

Y así termina el primer tramo del viaje de receso festivo invernal.

../galleries/Alghero/IMG_5663.JPG

desde aca hasta alla

* esta parte la deduje yo

** esta parte también deducida por quien escribe

Share