Thursday, June 14, 2012

test-driven infrastructure changes

I'm currently working a lot on infrastructure-related tasks. As a software developer I really appreciate TDD with all the certainty I get for code changes. Many changes of the infrastructure stack can be tested manually using curl. For instance to check if a request to a certain URL succeeds, this is all you need:

~ curl  -I "http://10.0.0.1/"
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 14 Jun 2012 10:19:33 GMT
...

You can easily add cookies, do virtual host routing and pretend SSL termination:

~ curl  -I -H"Host: awwsnap.io" -H"X-Forwarded-Proto: https" \
        -b"cookie=value" "http://10.0.0.1/"

Ok, that's old news. But, how about automating those tests and spec'ing your stack?

To accomplish this, I use roundup made by Blake Mizerany. It's a great testing framework that perfectly fits into UNIX environments. And its tests are written in plain bash. That's exactly what I need. Check it out, if you don't know it yet!

With roundup we can quickly put the example from before into a spec:

#!/usr/local/bin/roundup

### matchers

function returns {
  cat | head -n1 | grep $1
}

### spec

it_should_redirect_requests_to_root() {
  curl  -I -H"Host: awwsnap.io" -H"X-Forwarded-Proto: https" \
        -b"cookie=value" "http://10.0.0.1/" | returns 200
}

Roundup detects the success or failure of a test by its error status. In the previous example, the matcher function returns uses grep to find 302 in the first line of the response header. If grep finds that http status code it returns 0 otherwise 1.

This enables me to describe the wanted behaviour of a system up-front and "fix it" in a more focused way.
One additional benefit during a rollout of a new configuration is that you can now run the test repeatedly against a host to validate the changes.

To stimulate your imagination a bit more, here are some additional examples:

#!/usr/local/bin/roundup

### matchers

function returns {
  cat | head -n1 | grep $1
}

# check for rails-specific header
function is_rails {
  cat | grep "^X-Runtime: "
}

function redirects_to {
  res=$(cat)
  echo $res | returns 302 && echo $res | grep "Location: $1"
}

### helpers

# using tee makes debugging easier
function get {
  curl  -I -H"Host: awwsnap.io" -H "X-Forwarded-Proto: https" \
        -b"cookie=value" | tee /dev/stderr
}

### spec

it_should_serve_root() {
  get "http://10.0.0.1/" | returns 200
}

it_should_pass_to_rails_backend_for_admin() {
  get "http://10.0.0.1/admin" | is_rails
}

it_should_redirect_to_cdn_for_assets() {
  get "http://10.0.0.1/assets" | redirects_to "cdn.awwsnap.io"
}




1 comment:

  1. Nice one, didn't know about Roundup. The specs look clean and the code intuitive enough to get them right away, even though it's Bash :)

    ReplyDelete