# -*- mode: org; mode: auto-fill; -*- #+STARTUP: showeverything * Org Converge [[https://secure.travis-ci.org/wallyqs/org-converge.png?branch=master]] ** Description A framework to create documented reproducible runs using [[http://orgmode.org/worg/org-contrib/babel/Org Babel][Org Mode]], borrowing several ideas of what is possible to do with tools like =chef-solo=, =rake=, =foreman= and =capistrano=. ** Install : gem install org-converge ** Motivation [[http://orgmode.org/worg/org-contrib/babel/Org Babel][Org Mode]] has proven to be flexible enough to write [[http://www.jstatsoft.org/v46/i03][reproducible research papers]], then I believe that configuring and setting up a server for example, is something that could also be done using the same approach, given that /converging/ the configuration is a kind of run that one ought to be able to reproduce. Taking the original Emacs implementation as reference, Org Converge uses the [[https://github.com/wallyqs/org-ruby][Ruby implementation of the Org mode parser]] to implement and enhance the functionality from Org Babel by adding helpers to define the properties of a run while taking advantage of what is already there in the Ruby ecosystem. ** Usage examples Org Converge supports the following kind of runs below: *** Parallel runs Each one of the code blocks is run on an independent process. This is akin to having a =Procfile= based application, where one of the runner command would be to start a web application and the other one to start the a worker processes. In the following example, we are defining 2 code blocks, one that would be run using Ruby and one more that would be run in Python. Notice that the Python block has =:procs= set to 2, meaning that it would spawn 2 processes for this. #+begin_src sh ,#+TITLE: Sample parallel run   ,#+name: infinite-worker-in-ruby ,#+begin_src ruby $stdout.sync = true loop { puts "working!"; sleep 1; } ,#+end_src   ,#+name: infinite-worker-in-python ,#+begin_src python :procs 2 import sys import time while True: print "working too" sys.stdout.flush() time.sleep(1) ,#+end_src #+end_src The above example can be run with the following: : org-run procfile-example.org Sample output of the run: #+begin_src sh [2014-06-07T18:05:48 +0900] infinite-worker-in-ruby -- started with pid 19648 [2014-06-07T18:05:48 +0900] infinite-worker-in-python:1 -- started with pid 19649 [2014-06-07T18:05:48 +0900] infinite-worker-in-python:2 -- started with pid 19650 [2014-06-07T18:05:48 +0900] infinite-worker-in-python:1 -- working too [2014-06-07T18:05:48 +0900] infinite-worker-in-python:2 -- working too [2014-06-07T18:05:48 +0900] infinite-worker-in-ruby -- working! [2014-06-07T18:05:49 +0900] infinite-worker-in-python:1 -- working too [2014-06-07T18:05:49 +0900] infinite-worker-in-python:2 -- working too #+end_src *** Sequential runs In case the code blocks form part of a runlist that should be ran sequentially, it is possible to do this by specifying the ~runmode=sequential~ option. #+begin_src sh ,#+TITLE: Sample sequential run ,#+runmode: sequential   ,#+name: first ,#+begin_src sh echo "first" ,#+end_src   ,#+name: second ,#+begin_src sh echo "second" ,#+end_src   ,#+name: third ,#+begin_src sh echo "third" ,#+end_src   ,#+name: fourth ,#+begin_src sh echo "fourth" ,#+end_src #+end_src In order to specify that this is to be run sequentially, we set the runmode option in the command line: : org-run runlist-example.org --runmode=sequential Another way of specifying this is via the Org mode file itself: #+begin_src org ,#+TITLE: Defining the runmode as an in buffer setting ,#+runmode: sequential #+end_src Sample output: #+begin_src sh [2014-06-07T18:10:33 +0900] first -- started with pid 19845 [2014-06-07T18:10:33 +0900] first -- first [2014-06-07T18:10:33 +0900] first -- exited with code 0 [2014-06-07T18:10:33 +0900] second -- started with pid 19846 [2014-06-07T18:10:33 +0900] second -- second [2014-06-07T18:10:33 +0900] second -- exited with code 0 [2014-06-07T18:10:33 +0900] third -- started with pid 19847 [2014-06-07T18:10:33 +0900] third -- third [2014-06-07T18:10:33 +0900] third -- exited with code 0 [2014-06-07T18:10:33 +0900] fourth -- started with pid 19848 [2014-06-07T18:10:33 +0900] fourth -- fourth [2014-06-07T18:10:33 +0900] fourth -- exited with code 0 #+end_src *** Configuration management runs For example, using Org Babel tangling functionality we can spread config files on a server by writing the following on a ~server.org~ file... #+begin_src sh   Configuration for a component that shoul be run in multitenant mode:   ,#+begin_src yaml :tangle /etc/component.yml multitenant: false status_port: 10004 ,#+end_src #+end_src Then run: : sudo org-tangle server.org *Note:* The above tangle command is a reimplementation of the tangling functionality from Org mode using Ruby. If you are interested in tangling from the command line without losing functionality and have available a local Emacs Org mode install, the following project can be useful: *** Idempotent runs A run can have idempotency checks (similar to how the execute resource from [[http://docs.opscode.com/resource_execute.html][Chef]] works). An example of this, would be when installing packages. In this example, we want to install the =build-essential= package once, and skip it in following runs: #+begin_src sh ,** Installing the dependencies   Need the following so that ~bundle install~ can compile the native extensions correctly.    ,#+name: build-essential-installed ,#+begin_src sh dpkg -l | grep build-essential ,#+end_src     ,#+name: build_essentials ,#+begin_src sh :unless build-essential-installed apt-get install build-essential -y ,#+end_src    ,#+name: bundle_install ,#+begin_src sh cd project_path bundle install ,#+end_src #+end_src Furthermore,since we are using Org mode syntax, it is possible to reuse this setup file by including it into another Org file: #+begin_src sh ,#+TITLE: Another setup   Include the code blocks from the server into this:    ,#+include: "server.org"   ,#+name: install_org_mode ,#+begin_src sh apt-get install org-mode -y ,#+end_src #+end_src #+end_src Since this a run that involves converging into a state, it would be run sequentially with idempotency checks applied: : sudo org-converge setup.org *** Dependencies based runs In this type of runs we use the =:after= and =:before= header arguments to specify the prerequisites for a code block to run, similar to some of the functioality provided by tools like =rake= (Behind the scenes, these arguments create =Rake= tasks) In order for this kind of run to work, it has to be specified what is the task that we are converging to by using the =#+final_task:= in buffer setting: #+begin_src sh ,#+TITLE: Linked tasks example ,#+runmode: tasks ,#+final_task: final   ,#+name: second ,#+begin_src sh :after first for i in `seq 5 10`; do echo $i >> out.log done ,#+end_src   ,#+name: first ,#+begin_src ruby 5.times { |n| File.open("out.log", "a") {|f| f.puts n } } ,#+end_src   ,#+name: final ,#+begin_src python :after second :results output print "Wrapping up with Python in the end" f = open('out.log', 'a') f.write('11') f.close() ,#+end_src   ,#+name: prologue ,#+begin_src sh :before first :results output echo "init" > out.log ,#+end_src #+end_src : org-run chained-example.org --runmode=chained Instead of using =--runmode= options, it is also possible to just declare in buffer that the Org file should be run chained mode. #+begin_src org ,#+TITLE: Defining the runmode as an in buffer setting ,#+runmode: chained #+end_src Sample output: #+begin_src sh [2014-06-07T18:14:25 +0900] Running final task: final [2014-06-07T18:14:25 +0900] prologue -- started with pid 20035 [2014-06-07T18:14:25 +0900] prologue -- exited with code 0 [2014-06-07T18:14:25 +0900] first -- started with pid 20036 [2014-06-07T18:14:26 +0900] first -- exited with code 0 [2014-06-07T18:14:26 +0900] second -- started with pid 20038 [2014-06-07T18:14:26 +0900] second -- exited with code 0 [2014-06-07T18:14:26 +0900] final -- started with pid 20040 [2014-06-07T18:14:26 +0900] final -- Wrapping up with Python in the end [2014-06-07T18:14:26 +0900] final -- exited with code 0 #+end_src *** Remote runs For any of the cases above, it is also possible to specify whether the code blocks should be run remotely on another node. This is done by using =:dir= in the code block header argument. #+begin_src sh ,#+sshidentityfile: vagrant/keys/vagrant ,#+name: remote-bash-code-block   ,#+begin_src sh :results output :dir /vagrant@127.0.0.1#2222:/tmp random_number=$RANDOM for i in `seq 1 10`; do echo "[$random_number] Running script is $0 being run from `pwd`"; done ,#+end_src #+end_src Note that in order for the above to work, it is also needed to set identity to be used by ssh. *** Asserted runs In case the Org mode file has a results block which represents the expected result, there is an ~org-spec~ command which can be useful to check whether there was a change that no longer makes the results from the Org file valid. Example: #+begin_src sh ,#+TITLE: Expected results example    ,#+name: hello ,#+begin_src ruby :results output 10.times do puts "hola" end ,#+end_src    ,#+RESULTS: hello ,#+begin_example hola hola hola hola hola hola hola hola hola hola ,#+end_example #+end_src We can be able to verify whether this is still correct by running: : org-spec test.org #+begin_src sh Checking results from 'hello' code block: OK #+end_src As an example, let's say that the behavior of the original code block changed, and now says hello 5 times instead. In that case the output would be as follows: #+begin_src diff Checking results from 'hello' code block: DIFF @@ -1,11 +1,6 @@ -hola -hola -hola -hola -hola -hola -hola -hola -hola -hola +hello +hello +hello +hello +hello #+end_src ** Contributing The project is still in very early development and a proof of concept at this moment. But if you feel that it is interesting enough, please create a ticket to start the discussion.