Do you use let's encrypt?

4954 votes ~ 18 comments


A brief introduction to server-testing with serverspec

Posted by Steve on Thu 13 Mar 2014 at 10:16

Serverspec is the name of a Ruby tool which allows you to write simple tests, to validate that a server is correctly configured. Here we'll walk through the installation of the tool, and show how it can be used to test a server setup.

These days many tools exist to automate server configuration, for example Ansible, CFEngine, Chef, Puppet, and even my own Slaughter-tool.

Picking an automation tool is almost always a good idea, and it is largely irrelevant which one you choose, so long as you can find some local knowledge, or decent documentation, but that said it is very useful to be able to validate that your policies, recipes, or configuration-changes were actually applied as you expect.

ServerSpec is designed to allow testing the state of a series of remote hosts. You express how your server should be setup, and it will test that the current-setup matches your expectations.

Because this is a Ruby-based tool the installation might seem a little odd, as we need to use a couple of Ruby specific tools, but that said we're not going to install anything site-wide, so everything is neat and self-contained.

This introduction will do three things:

  • Cover the installation of the tool, and how it is launched.
  • Briefly demonstrate the testing it can perform.
  • Point to the documentation for adding more testing to your recipes.

We will assume you're running the tests from a Debian host, and that there exists at least one remote host you can connect to, with public-key auth, via SSH.

Installing serverspec

Because the serverspec tool is written in Ruby, and is distributed as a "gem" (which is the name given to self-contained Ruby libraries, tools, and extensions), we'll need to ensure we have the Ruby dependencies present:

root@host ~ # apt-get install ruby rubygems bundler

NOTE: Some Ubuntu systems will call the bundler package ruby-bundler instead.

Now that we have the system up to date we can create a directory to contain our tests and the downloaded gems:

user@host $ mkdir ~/spec-tests/
user@host $ cd ~/spec-tests/

At that point you can save the following contents to the ~/spec-tests/Gemfile, this file is used to contain dependencies in the Ruby-world.

source ''

gem 'serverspec'
gem 'rake'

With this Gemfile in-place you can now configure the libraries to be downloaded beneath your test-directory, and this is a good thing because it means you don't need to be root, nor do you need to worry about the complications of system-wide gem installation and maintainance.

To download the gems run:

user@host $ cd ~/spec-tests/
user@host $ bundle  install --path ./gems/
Fetching source index for
Installing rake (10.1.1)
Installing diff-lcs (1.2.5)
Installing highline (1.6.21)
Installing net-ssh (2.8.0)
Installing rspec-core (2.14.8)
Installing rspec-expectations (2.14.5)
Using bundler (1.0.15)
Your bundle is complete! It was installed into ./gems

At this point you have the dependencies installed, and you should be able to create a new spec (i.e. the tests we'll run against the remote server(s)).

Creating a trivial serverspec test

Now that you have serverspec installed you can the helper it installed to create your first test. We'll pretend we're testing that there is a HTTP server running on the host, which is a host you can connect to via ssh, with no password being required

With that in mind please run:

user@host ~ $ bundle exec serverspec-init
Select OS type:
  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name:
 + spec/
 + spec/

As you can see this prompted us with some simple questions, most obviously the hostname we're testing against, and how we connect to it. (We chose "ssh").

The end result will be a series of files and directories created:

|-- Gemfile
|-- Gemfile.lock
|-- Rakefile
`-- spec
    |   `-- httpd_spec.rb
    `-- spec_helper.rb

The most important of these files is the one that contains the tests we'll run, that is the file spec/ because each set of tests is contained in a sub-directory named after the hostname it applies to.

In addition to creating the spec/test file, it also created a top-level Rakefile. rake-files are like make-files, but written in Ruby. You can ignore it if you so wish, the only thing you need to know how to do is run the thing:

user@host $ bundle exec rake spec

What did that do? It used the "bundle exec" command, which wrapped up our dependencies from where we installed them, and executed "rake spec". The rake command read the Rakefile and executed the task with the name spec.

You don't necessarily need to know this, or remember it, just type "bundle exec rake spec" when you want to test your rules and all will be well.

If you run this test now it will probably fail, that is because the default spec/test file will assume it is running on a non-Debian system - and that the webserver comes from the httpd package.

If you open up ~/spec-test/spec/ and change it to read as follows :

require 'spec_helper'

describe package('apache2') do
  it { should be_installed }

describe service('apache2') do
  it { should be_enabled   }
  it { should be_running   }

describe port(80) do
  it { should be_listening }

This can now be executed:

$ bundle exec rake spec
/usr/bin/ruby1.8 -S rspec spec/

Finished in 1.63 seconds
4 examples, 0 failures

More Complex Recipes

I whipped up some simple recipes to describe the expected state of the cluster which powers this webste.

Although sharing this is a little pointless because none of you should be able to SSH into my servers (I hope!) I will share them in the spirit of completeness.

The tree looks just like those we've already seen, one spec-file for each host in the cluster which we're running a test against, along with the top-level Rakefile to initiate the testing, and the Gemfile I used to install the dependencies:

-- Gemfile
-- Gemfile.lock
-- Rakefile
-- spec
   |   `-- mysql_spec.rb
   |   `-- web_spec.rb
   |-- spec_helper.rb
   |   `-- web_spec.rb
   |   `-- web_spec.rb
   |   `-- web_spec.rb
       `-- web_spec.rb

If you wish to examine things you may browse the tests on github, or clone them locally:

user@host $ git clone

For me I can carry out the tests like so:

user@host $ bundle exec rake spec
/usr/bin/ruby1.9.1 -S rspec spec/ spec/ ...

Finished in 12.27 seconds
43 examples, 0 failures

Further Tests

The recipe which was installed when we ran the helper, serverspec-init, performed only minimal testing (that a webserver service was both present and enabled, and that a service was listening upon a port), but there are many more interesting things that you can test:

  • Test that commands return particular output.
  • Test that cron-jobs are present.
  • Test network details against interfaces.
  • Test that files/directories exist, and have the correct permissions.

These primitives are called "resource types" in serverspec, and you can find a list of them online at the serverspec homepage.



Re: A brief introduction to server-testing with serverspec
Posted by Anonymous (196.15.xx.xx) on Thu 18 Jun 2015 at 06:15
What if the host needs a password to connect??
I run the following comment
bundle exec rake spec

It asks for password, but is stuck after that.
Should I run
/usr/bin/ruby -S rspec spec/hostname/xx.rb
After giving the password??

[ Parent | Reply to this comment ]