How many times have you had a situation when you open a file for editing, make a bunch of changes, and discover that you don't have the rights to write the file? This happens to me a lot.

It usually goes like this. You open a file and you forget to use sudo:

$ vim /etc/apache/httpd.conf

You make many changes and then you type:

:wq!

And you get an error:

"/etc/apache/httpd.conf" E212: Can't open file for writing
Press ENTER or type command to continue

And then you go like, duh. At this point you either quit vim:

:q!

And open the file again with sudo:

$ sudo vim /etc/apache/httpd.conf

And make all the changes again. Or if you're a bit smarter, you save the file to /tmp directory:

:w /tmp/foo

And then you sudo move the /tmp/foo to the right location:

$ sudo mv /tmp/foo /etc/apache/httpd.conf

Don't do that anymore! Use this command:

:w !sudo tee % >/dev/null

This command will save you hundreds of hours throughout your career. Here's how it works - vim spawns sudo tee FILENAME and pipes the contents of the file to its stdin. The tee command now runs in a privileged environment and redirects its stdin to FILENAME. The >/dev/null discards tee's stdout as you don't need to see it.

In fact, don't use this command as it's too too long and complicated to remember! Save another few hundred hours and create a vim alias for this command because you'll use it for the rest of your life. Put this in your ~/.vimrc:

cnoremap sudow w !sudo tee % >/dev/null

Now the next time you're in this situation, just type:

:sudow

See you!

Here's another interesting story about how we do things at Browserling. This time the story is about how we use ploy to deploy code at Browserling and Testling.

First off, what is ploy? Ploy is a deployment system that we created at Browserling, that includes a http router, a git endpoint and a process manager all in one. You just push your code at ploy, and it deploys it and runs the necessary processes.

Ploy overview

Here's a simple overview and an example of how to get started with ploy. First, setup auth.json that ploy will use for git authentication when you git push at it:

$ cat auth.json
{
  "pkrumins" : "password",
  "substack" : "password2"
}

Then setup a git remote in your app's repository:

$ git remote add ploy http://pkrumins:password@myapp.com/_ploy/myapp.git

Ploy uses the special /_ploy/NAME.git url as a git endpoint.

Then start ploy:

$ ploy ./myapp -a auth.json --port 80

This will start ploy on port 80 and use ./myapp directory to store the repository and logs. Surely you never want to run any code as a privileged user, so see the section below on how to setup ploy on port 8080 and redirect port 80 with iptables to port 8080.

Your app should have a package.json file, such as the following:

{
  "name": "myapp",
  "private": true,
  "version": "0.0.1",
  "scripts": {
    "start": {
      "index": "node server.js",
      "stats": "node stats/server.js",
      "_auth": "node auth/server.js"
    },
    "postinstall": "./install.sh"
  }
}

The scripts.start lists processes that ploy will manage. The index process will handle requests to www.myapp.com and myapp.com, whereas stats will handle requests to the stats.myapp.com subdomain. The process starting with an underscore _auth is a non-http process that will simply be run by ploy and won't be mapped to any http requests.

Now git push your code to ploy:

$ git push ploy master

When you push code at a ploy server, your processes will be started and any previous processes running under the same branch name will be killed. Ploy does not detach your server processes. When the ploy server goes down, it takes the processes it started with it. However, when the ploy server is started back up, it will attempt to restart all of the processes it was previously running.

Before starting the processes, ploy will run npm install . that will handle the scripts.postinstall (also install and preinstall) hooks. Ploy'll set the PORT environment variable that your application should listen to. It will then route all the http requests on port 80 to your application listening on PORT.

You can also setup staging branches easily with ploy, just push to ploy like this:

$ git push ploy master:staging

This will make the staging branch available at staging.myapp.com (given that you've an A DNS record for staging.myapp.com that points to the same server that myapp.com does). The stats process will be available at staging.stats.myapp.com.

Once you're done with your staging branch, you can simply move staging to production (master branch) by running ploy mv staging master. That will re-route the connections and you'll instantly be running your staging in production.

Ploy provides many commands for managing processes, logs, and branches. You can list the processes by running ploy ls from your repository. You can restart them by running ploy restart <process name>. You can view the logs through ploy log, and manage branches branches through ploy mv, ploy rm commands, and ploy redeploy commands.

Ploy makes it really easy to deploy your apps to production and staging! Read more about it on ploy's project page!

Ploy at Browserling and Testling

Now let's turn to how we use ploy in production to deploy Browserling and Testling. We've a more complex ploy setup, where we use ssl certificates for running https, and a request router for more complicated routing.

Here's how we run ploy for Testling:

ploy ./testling \
  -a auth.json \
  --port 8000 \
  --sslPort 8443 \
  --ca=./certs/bundle.crt \
  --cert=./certs/ci_testling_com.crt \
  --key=./certs/server.key \
  -f testling-router.js
  • The -a argument specifies the authentication info for the git endpoint.
  • The --port argument specifies the http port that ploy will listen on.
  • The --sslPort argument specifies the https port that ploy will listen on.
  • The --ca, --cert, and --key argument setup the ssl cert.
  • The -f argument specifies the http router that ploy will use to route the connections.

We run ploy as non-root and it listens on port 8000 for http connections, and on port 8443 for https connections. We've the following iptables rules setup on the testling server that redirect ports 80 to 8000, and 443 to 8443:

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8000
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8000
iptables -t nat -I OUTPUT -p tcp -d testling.com --dport 80 -j REDIRECT --to-ports 8000

iptables -A PREROUTING -t nat -p tcp --dport 443 -j REDIRECT --to-port 8443
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 8443
iptables -t nat -I OUTPUT -p tcp -d testling.com --dport 443 -j REDIRECT --to-ports 8443

Auth file auth.json contains the user access info, in our case it's just me and substack:

{ "pkrumins": "pass1", "substack": "pass2" }

The router file is used in more complicated http routing cases. If we didn't have the testling-router.js router, then all the requests to ports 80 and 443 would simply be routed to app's PORT (environment variable that was set by ploy before it started the processes), but in our case we redirect all unencrypted http connections to https:

module.exports = function (req, res, bounce) {
    if (!req.connection.encrypted) {
        if (req.headers.host) {
            var hostname = req.headers.host.split(':')[0];
            if (hostname == 'ci.testling.com') {
                for (var i = 0; i < this.bouncers.length; i++) {
                    if (!this.bouncers[i].key) continue;
                    
                    res.statusCode = 302;
                    res.setHeader('location', 'https://ci.testling.com' + req.url);
                    return res.end();
                }
                res.statusCode = 404;
                return res.end('no https endpoint configured\n');
            }
        }
    }
    bounce()
};

What this router does is it checks if a connection is encrypted, and if it's not, it simply sets the Location: https://ci.testling.com/URL header and the browser redirects the visitors to https.

You can do all kinds of crazy things with a router, for example, route based on the source/destination IP, or route based on http headers, route based on date/time, etc.

Here's how the root directory package.json looks for Testling:

{
  "name": "testling-ci",
  "private": true,
  "version": "0.0.1",
  "scripts": {
    "start": {
      "ci": "node ci/server.js",
      "git": "node git/server.js"
    },
    "postinstall": "./install.sh"
  }
}

And here's how the postinstall script looks like:

#!/bin/bash
#

(cd ci; npm install .)
(cd git; npm install .)

What this script does is it simply goes into the ci/ and git/ directories and npm installs the modules. After it's done, ploy starts the scripts.start.ci and scripts.start.git processes that map to http://ci.testling.com and http://git.testling.com subdomains.

Browserling uses pretty much the same arguments to ploy but has a much more complicated router that uses seaport:

var seaport = require('seaport');
var config = require('figc')(__dirname + '/config.json');
var pick = require('deck').pick;

var ports = seaport.connect(config.seaport);
ports.on('connect', function () { console.log('browserling-router.js: connected to seaport') });
ports.on('disconnect', function () { console.log('browserling-router.js: disconnected from seaport') });

module.exports = function (req, res, bounce) {
    if (req.headers.host && req.headers.host.split(':')[0] == 'status.browserling.com') {
        var ps = ports.query('web.status')
        if (ps.length) {
            return bounce(pick(ps), {
                headers: { 'x-forwarded-for': req.connection.remoteAddress }
            });
        }
        res.statusCode = 404;
        return res.end('no matching services are running for status.browserling.com');
    }

    if (req.headers.host && req.headers.host.split(':')[0] == 'browserling.com') {
        console.log(req.method + ' request from ' + req.connection.remoteAddress + ' to ' + req.url);
        if (req.connection.encrypted || req.url == '/account/verify.json') {
            var ps = ports.query('web.browserling');
            if (ps.length) {
                return bounce(pick(ps), {
                    headers: { 'x-forwarded-for': req.connection.remoteAddress }
                });
            }
            res.statusCode = 404;
            return res.end('no matching services are running for browserling.com\n');
        }
        else {
            res.statusCode = 302;
            res.setHeader('location', 'https://browserling.com' + req.url);
            return res.end();
        }
    }
    else if (req.headers.host && req.headers.host.split(':')[0] == 'www.browserling.com') {
        res.statusCode = 302;
        res.setHeader('location', 'https://browserling.com' + req.url);
        return res.end();
    }

    bounce()
};

At Browserling all our services are registered with seaport, so the routing no longer happens to the port in the PORT environment variable. Instead the the application's port is requested through seaport's ports.query. I'll write more about how we use seaport at Brwoserling some time later.

Ploy has been really wonderful to work with. If you haven't used it and you're looking for a deployment system for node (and not just node!), definitely try it out!

Until next time!

We recently added invoices to Browserling. I thought I'd share how we did it as it's another interesting story.

Our customers keep asking for invoices all the time so we made it simple to create them. They can now just go to their Browserling accounts and download them. Here's how an invoice looks like:


Example invoice. (Download example.)

And here are the implementation details.

We use a node module called invoice. The invoice module takes a hash of invoice details, internally spawns pdflatex that creates a pdf invoice, and then calls a callback with the path to the pdf file, like this:

var invoice = require('invoice');
invoice(
    {
        template: 'browserling-dev-plan.tex',
        from: "Browserling inc\\\\3276 Logan Street\\\\Oakland, CA 94601\\\\USA",
        to: "John Smith\\\\Corporation Inc.",
        period: "2/2013",
        amount: "$20"
    },
    function (err, pdf) {
        if (err) {
            console.log("Failed creating the invoice: " + err);
            return;
        }
        console.log("Pdf invoice: " + pdf);
    }
);

The browserling-dev-plan.tex latex template contains %to%, %from%, %period%, and %amount% place holders that the invoice module simply replaces with the given data:

\documentclass[12pt]{article}

\pagenumbering{gobble}

\begin{document}

\begin{center}
\Large{\textbf{Invoice}} \\
\vspace{0.5cm}
\large{Subscription to Browserling's Developer Plan}
\end{center}

\vspace{1cm}

\section*{Invoice from:}
%from%

\section*{Invoice to:}
%to%

\section*{Period:}
%period%

\section*{Amount:}
%amount%

\end{document}

Once the pdf invoice is generated, we create a token that maps to the pdf file, and once it's requested, we send it to the customer as an application/pdf.

Until next time!

This is going to be a quick tutorial on how to run multiple node versions side by side. There are many different ways to do it but this works well for me.

First I compile node versions from source and I set them up in the following directory structure:

/home/pkrumins/installs/node-v0.8.20
/home/pkrumins/installs/node-v0.8.21
/home/pkrumins/installs/node-v0.10.3
/home/pkrumins/installs/node-v0.10.22

When compiling node I simply specify --prefix=/home/pkrumins/installs/node-vVERSION, and make install installs it into that path.

Next I've this bash alias:

function chnode {
  local node_path="/home/pkrumins/installs/node-v$1/bin"
  test -z "$1" && echo "usage: chnode <node version>" && return
  test ! -d "$node_path" && echo "node version $1 doesn't exist" && return
  PATH=$node_path:$PATH
}

Now when I want to run node 0.8.21, I run chnode 0.8.21 to update the path:

$ chnode 0.8.21
$ which node
/home/pkrumins/installs/node-v0.8.21/bin/node
$ node --version
v0.8.21

Or if I want to run node 0.6.18, I run chnode 0.6.18:

$ chnode 0.6.18
$ which node
/home/pkrumins/installs/node-v0.6.18/bin/node
$ node --version
v0.6.18

Works for me both locally and in production. Until next time.

Here is a neat trick. If you want to start a program that always respawns if it gets killed, just put it in /etc/inittab. The init process will respawn the program. That's what it's for.

Here's an example. Let's say you want /bin/unkillable to always run. Put this in /etc/inittab:

uniq:3:respawn:/bin/unkillable

Then run init q to make init re-read the inittab file. Now whenever /bin/unkillable gets killed, init will respawn it.

The init process uses this same trick to spawn terminals (otherwise if the terminals died and no one respawned them, no one would be able to log in from the physical terminals). This approach is also very useful if you absolutely must have some programs running. You don't even need complicated tools such as daemontools or supervisor to respawn programs. In most cases this trick is enough.

More information about init and /etc/inittab can be found in man init and man inittab. Until next time.

Footnote:
* by unkillable I mean one that respawns when you kill it.