Official release post: http://willcodeforfoo.com/2009/07/13/announcing-alice/
As a queue server, RabbitMQ is super cool, but my company is hesitant to use it without a nice front-end or access to statistics about the server. So we set out to develop the latest RabbitMQ REST interface, Alice.
Alice is a RESTful interface to the RabbitMQ server that talks directly through erlang's native interface, epmd. The purely RESTful server responds to the same interface as the RabbitMQ's command-line interface and presents a native HTTP interface to the data. Alice is written with Mochiweb.
How to get started.
git clone git://github.com/auser/alice.git cd alice ./start.sh You can pass a rabbithost where your rabbitmq-server sits by passing the options rabbithost in the command-line, like so:
./start.sh -alice rabbithost "other.node.come" Note, you may have to set your cookie when starting Alice with a remote node by the -setcookie flag:
./start.sh -alice rabbithost "other.node.com" -setcookie "mysecretcookie" ## Currently exposed RESTful routes /conn - Current connection information /exchanges - Current exchanges information /queues - Current queues /users - Current users /bindings - Current bindings /control - Access to the RabbitMQ control /permissions - Current permissions /vhosts - Current vhosts These endpoints all are exposed with the four verbs (get, post, put, delete) and respond in the JSON format, (except the root / endpoint which responds with text/html).
# List users curl -i http://localhost:9999/users HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:08:20 GMT Content-Type: text/json Content-Length: 19 {"users":["guest"]} # Viewing a specific user curl -i http://localhost:9999/users/guest HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:01:01 GMT Content-Type: text/json Content-Length: 17 {"users":"guest"} # If the user is not a user: curl -i http://localhost:9999/users/bob HTTP/1.1 400 Bad Request Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:01:20 GMT Content-Type: text/json Content-Length: 20 {"bob":"not a user"} # Add a user curl -i -XPOST \ -d'{"username":"ari", "password":"weak password"}' \ http://localhost:9999/users HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Thu, 16 Jul 2009 00:10:35 GMT Content-Type: text/json Content-Length: 25 {"users":["ari","guest"]} # Deleting a user curl -i -XDELETE http://localhost:9999/users/ari HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:19:24 GMT Content-Type: text/json Content-Length: 19 {"users":["guest"]} Notice that when we list the user that doesn't exist, bob from the second example above, the return is a 400. This is especially useful when you want to access the data programmatically. More on extending Alice below and how to get access to the return value of the requested route.
The same basic usage is applied to all the routes listed, as you can see:
# List connections curl -i http://localhost:9999/conn HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:30:52 GMT Content-Type: text/json Content-Length: 287 {"conn":[{"pid":"...","ip":"127.0.0.1","port":"5672","peer_address":"127.0.0.1" ...}]} # List the current exchanges curl -i http://localhost:9999/exchanges HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:34:14 GMT Content-Type: text/json Content-Length: 654 {"exchanges":[{"name":"amq.rabbitmq.log","type":"topic","durable":"true","auto_delete":...} # List the current queues curl -i http://localhost:9999/queues HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:35:42 GMT Content-Type: text/json Content-Length: 60 {"queues":[{"memory":"212988","name":"noises","vhost":"/"}]} # List the current bindings curl -i http://localhost:9999/bindings HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:36:13 GMT Content-Type: text/json Content-Length: 69 {"bindings":[{"queue":"noises","exchange":"","from_queue":"noises"}]} # List permissions curl -i http://localhost:9999/permissions HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:37:32 GMT Content-Type: text/json Content-Length: 42 {"permissions":{"vhosts":[{"name":"/", "users":[{"name":"guest","configure":".*","read":".*","write":".*"}]}]}} # You can list permissions for a user curl -i http://localhost:9999/permissions/amr HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:50:33 GMT Content-Type: text/json Content-Length: 42 {"permissions":{"name":"guest","vhosts":[{"name":"/","configure":".*","write":".*","read":".*"}]}} # You can list permissions on a vhost too curl -i http://localhost:9999/permissions/vhost/root HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:50:33 GMT Content-Type: text/json Content-Length: 42 {"permissions":{"name":"/","users":[{"name":"guest","configure":".*","write":".*","read":".*"}]}} # Setting permissions curl -i -XPOST -d '{"vhost":"/", "configure":".*", "read":".*", "write":".*"}' \ http://localhost:9999/permissions/guest HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:55:33 GMT Content-Type: text/json Content-Length: 38 {"permissions":{"name":"guest","vhosts":[{"name":"/","configure":".*","write":".*","read":".*"}]}} # Deleting permissions curl -i -XDELETE -d '{"vhost":"/"}' http://localhost:9999/permissions/guest HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:55:33 GMT Content-Type: text/json Content-Length: 38 {"permissions":{"name":"guest","vhosts":[]}} # List vhosts curl -i http://localhost:9999/vhosts HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:57:10 GMT Content-Type: text/json Content-Length: 16 {"vhosts":["/"]} # Viewing a specific vhost curl -i http://localhost:9999/vhosts/barneys%20list HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:59:29 GMT Content-Type: text/json Content-Length: 25 {"vhosts":"barneys list"} # If it doesn't exist: curl -i http://localhost:9999/vhosts/barneys%20listings HTTP/1.1 400 Bad Request Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:59:59 GMT Content-Type: text/json Content-Length: 34 {"barneys listings":"not a vhost"} # Add a vhost curl -i http://localhost:9999/vhosts -XPOST -d'{"name":"barneys list"}' HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 07:58:09 GMT Content-Type: text/json Content-Length: 31 {"vhosts":["/","barneys list"]} # Delete a vhost curl -XDELETE -i http://localhost:9999/vhosts/barneys%20list HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:02:44 GMT Content-Type: text/json Content-Length: 16 {"vhosts":["/"]} Now, there is a module in the Alice called control. There are a lot of routes and a lot of functionality built-in here, so let's dig in.
# Getting the status of the server curl -i http://localhost:9999/control HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:05:19 GMT Content-Type: text/json Content-Length: 151 {"status":[{"applications":["rabbit","mnesia","os_mon","sasl","stdlib","kernel"], \ "nodes":["rabbit@YPCMC05591"],"running_nodes":["rabbit@YPCMC05591"]}]} # Stopping the rabbitmq-server curl -XPOST -i http://localhost:9999/control/stop HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:06:02 GMT Content-Type: text/json Content-Length: 20 {"status":"stopped"} # Starting the rabbitmq-server application curl -XPOST -i http://localhost:9999/control/start_app HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:06:50 GMT Content-Type: text/json Content-Length: 20 {"status":"started"} # Stopping the rabbitmq-server application curl -XDELETE -i http://localhost:9999/control/stop_app HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:15:56 GMT Content-Type: text/json Content-Length: 20 {"status":"stopped"} # Reset the rabbitmq-server application curl -XPOST -i http://localhost:9999/control/reset HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:07:15 GMT Content-Type: text/json Content-Length: 18 {"status":"reset"} # Or force-resetting the server curl -XPOST -i http://localhost:9999/control/force_reset HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:07:27 GMT Content-Type: text/json Content-Length: 18 {"status":"reset"} # Clustering a set of nodes curl -XPOST -i http://localhost:9999/control/cluster -d'{"nodes":["bob@otherhost"]}' HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:14:10 GMT Content-Type: text/json Content-Length: 20 {"status":"cluster"} # Rotating rabbit logs curl -XPOST -i http://localhost:9999/control/rotate_logs -d'{"prefix":"mn_"}' HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:15:12 GMT Content-Type: text/json Content-Length: 25 {"status":"rotated_logs"} Alice is written with the intention of being highly extensible and makes it easy to do so. The controllers respond only to the four verbs with pattern-matching on the routes.
For instance, a very basic controller looks like this:
-module (say). -export ([get/1, post/2, put/2, delete/2]). get([]) -> {"hello", <<"world">>}; get(_Path) -> {"error", <<"unhandled">>}. post(_Path, _Data) -> {"error", <<"unhandled">>}. put(_Path, _Data) -> {"error", <<"unhandled">>}. delete(_Path, _Data) -> {"error", <<"unhandled">>}. There are the 4 RESTful verbs that the controller responds. Now, if you were to compile this in Alice (in src/rest_server/controllers), then the route http://localhost:9999/say would now be accessible. Cool!
curl -i http://localhost:9999/say HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:20:57 GMT Content-Type: text/json Content-Length: 17 {"hello":"world"} Now let's add a route to say hello to someone:
-module (say). -export ([get/1, post/2, put/2, delete/2]). get([Name]) -> {"hello", erlang:list_to_binary(Name)}; get([]) -> {"hello", <<"world">>}; % .... curl -i http://localhost:9999/say/ari HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:21:54 GMT Content-Type: text/json Content-Length: 15 {"hello":"ari"} Finally, with every other verb than get, we are given data to extract. Let's see how to pull some data in a post. The data is given as a proplist with binary keys, we it's pretty easy to pull them out:
% ... post([], Data) -> Name = erlang:binary_to_list(proplists:get_value(<<"name">>, Data)), {"hello back", erlang:list_to_binary(Name)}; post([]) -> % ... Let's check it:
curl -i http://localhost:9999/say -XPOST -d'{"name":"ari"}' HTTP/1.1 200 OK Server: MochiWeb/1.0 (Any of you quaids got a smint?) Date: Tue, 04 Aug 2009 08:23:54 GMT Content-Type: text/json Content-Length: 20 {"hello back":"ari"} It's as easy as pie to extend Alice.
Wonderland is the webUI to Alice. It is driven by the javascript framework Sammy and Alice in the backend. Because the framework is client-side and accesses the data through ajax, Wonderland can be deployed nearly anywhere.
cd alice make wonderland - Issue tracker
- Google group
- irc: irc.freenode.net / #poolpartyrb
Or feel free to ping me on email (arilerner dot mac dot com) if you have any questions.