telegraft

node.js ZEROMQ mesh network

#telegraft Telegraft broadcasts configuration and setup tasks to drone servers.

It is heavily inspired by the likes of Puppet and Chef but leans much more towards Ansible in terms of a serverless deployment mechanism.

  npm install telegraft

You use telegraft by broadcasting to listeners. The listeners are servers and the broadcasts are a sequence of SSH/SCP commands.

The first thing we do in any telegraft project:

var telegraft = require('telegraft');

Now we want to make a recording which is a re-usable function that defines a certain action to be carried out on a server. Recordings can use other recordings and because they are just functions, the composition is up to you (powertool!).

Each recording function accepts a broadcast object and will use 2 main functions on it:

  • record: logs a command to be carried out on a server
  • wait: waits until the previous commands have finished on ALL servers

Here is an example of a very basic recording that outputs 'Hello World' to a temp file, waits and then deletes it.

var recordingTest = function(broadcast) {
    broadcast.record('echo "Hello World" > /tmp/telegraft.txt');
    broadcast.wait();
    broadcast.record('rm -f /tmp/telegraft.txt');
};

To broadcast this recording to our localhost (it will just use the local shell):

telegraft(recordingTest).transmit('localhost')

To broadcast this recording to remote hosts (you must have SSH keys in place):

telegraft(recordingTest).transmit('remotehost')

The above example is all nice and dandy but a bit pointless on one server - why are we calling wait() in the example above?

The power of telegraft comes when you wanted to do the above on 50 servers.

Imagine that it was ESSENTIAL that all 50 servers had written the test file before ANY of them start deleting it (I know, it's a simple example but use your imagination for large compilcated deployments):

telegraft(recordingTest).transmit([
    'server1.me.com',
    'server2.me.com',
    'server3.me.com',
    '...'
])

Now you have a wonderfully orchestrated 2 step process of adding then removing a file co-ordinated across 50 servers simultenously!

The usage pattern that makes this powerful is to have a recording that 'prepares' servers, then waiting for all servers to finish preparing and then trigger an update

Recordings are just functions. The idea is that you can organize recordings and include libraries of them in any manner you see fit.

When you call broadcast.record - it will behave differently depending on the first argument:

This is the most basic and low level type of recording, it basically represents a single SSH or SCP command.

// send an SSH command down the wire
broadcast.record('ssh', 'uptime');

// send an SCP command down the wire
broadcast.record('scp', '...');

// assume an SSH command if none given
broadcast.record('uptime'); 

This function will be run and given a broadcast object just like any other recording. This allows you to group commands and include libraies.

broadcast.record(function(){
    broadcast.record('echo "Hello World" > /tmp/telegraft.txt');
    broadcast.wait();
    broadcast.record('rm -f /tmp/telegraft.txt');
})

This will be iterated and each item passed to record.

This example uses the 'service' recording from the archive to restart two services in order.

broadcast.record([

    telegraft.archive.service({
        name:'httpd',
        action:'restart'
    }),

    // this lets us wait even in array trim
    telegraft.wait(),

    telegraft.archive.service({
        name:'myapp',
        action:'restart'
    })

]);

It's always a good idea to have your recording functions wrapped in a closure - this lets you use the same recording but with different data (this is how recordings from the archive work).

Take our example above:

var recordingTest = function(broadcast) {
    broadcast.record('echo "Hello World" > /tmp/telegraft.txt');
    broadcast.wait();
    broadcast.record('rm -f /tmp/telegraft.txt');
};

To make this more useful:

var testFileCreation = function(path, content){
    return function(broadcast){
        broadcast.record('echo "' + content + '" > ' + path);
        broadcast.wait();
        broadcast.record('rm -f ' + path);
    }
}

broadcast.record(testFileCreation('/tmp/telegraft.txt', 'Hello World'));

A classic use case might be to make a recording that uploads some new virtual host config file and then restarts apache.

So we make a closure that accepts the variables and returns the recording function.

// a closure function that accepts a list of website configs and returns
// a function that will record everything on the broadcast
var addVirtualHosts = function(websites) {

    // here is the return function that will be run by telegraft with a broadcast
    return function(broadcast) {

        // we want to add the config files for each website
        for(var i in websites) {
            var website = websites[i];

            var httpConfigDev = '/etc/httpd/sites-available/' + website.name;
            var httpConfigLive = '/etc/httpd/sites-enabled/' + website.name;

            // use an archive recording to upload the virtual host config file for this website
            broadcast.record(telegraft.archive.template({
                source:'./templates/apacheVHost', 
                dest:httpConfigDev,
                vars:{
                    host:website.domain,
                    document_root:'/home/' + website.name + '/website/www'
                }
            }));

            // symlink the virtual host file so it loads up
            broadcast.record('ln -s ' + httpConfigDev + ' ' + httpConfigLive);
        }

        // we want to wait now for all of the configs to be prepared
        broadcast.wait();

        // now we restart all the servers
        broadcast.record(telegraft.archive.service({
            name:'httpd',
            action:'restart'
        });

    };

}

To use this:

broadcast.record(addVirtualHosts([
    {
        name:"bob",
        domain:"bob.com"
    },
    {
        name:"dave",
        domain:"dave.com"
    }

]));

Telegraft is a way of remotely configuring servers using SSH.

It is inspired heavily by Ansible

Components at play in a telegraft network:

  • Transmitter: the control server(s) that are broadcasting commands

  • Receiver: the servers that are recieving commands

  • Recording: some work to be done to a server

  • Broadcast: sending out a recording to a particular server

  1. The Transmitter (Tx) is setup

  2. SSH access from the Tx to the Receivers (Rx's) is enabled

  3. Recordings are coded and then transmitted to certain Receivers

  4. This results in SSH & SCP commands being co-ordinated in sequence

  5. The broadcast can then summarize what happened

JQuarry follow jquarry on twitter @quarryjs

Licenced under the MIT License