Five minutes with testinfra

Continuing my journey through infrastructure testing tools we next visit testinfra, a serverspec equivalent written in python. For continuity purposes we’ll redo the Redis tests from the previous blog post.

First we’ll configure a testinfra virtualenv we can use for our experiments.

$ virtualenv testinfra-py-redis
New python executable in testinfra-py-redis/bin/python2

$ cd testinfra-py-redis

$ source bin/activate
(testinfra-py-redis)[dwilson@home testinfra-py-redis]$

$ pip install testinfra

# prove it works
$ testinfra --version

Now we have a working install of testinfra we’ll write some tests for redis. This test code is very close to normal python, there are few fancy abstractions. On the plus side this, and the library being very internally consistent, means anyone with basic python skills will be up and running very quickly.

$ cat > test_redis.py <<EOF

def test_redis_is_installed(Package):
    redis = Package("redis")
    assert redis.is_installed


def test_config_file(File):
    config_file = File('/etc/redis.conf')
    assert config_file.contains('bind 127.0.0.1')  # todo make it a regex
    assert config_file.is_file


def test_service_running(Service):
    service = Service('redis')
    assert service.is_running


def test_socket_listening(Socket):
    socket = Socket('tcp://127.0.0.1:6379')
    assert socket.is_listening


def test_command_output(Command):
    command = Command('redis-cli ping')
    assert command.stdout.rstrip() == 'PONG'
    assert command.rc == 0

EOF

At this point the library’s installed and we’ve written some tests. All that’s left is to run them. In the examples below we’ll show both a normal, but verbose, run and the Nagios compatibility mode for integrating testinfra with your monitoring system.

$ testinfra -v test_redis.py
==== test session starts ====
plugins: testinfra-1.0.0.0a12.dev3
collected 5 items

test_redis.py::test_redis_is_installed[local] PASSED
test_redis.py::test_config_file[local] PASSED
test_redis.py::test_service_running[local] PASSED
test_redis.py::test_socket_listening[local] PASSED
test_redis.py::test_command_output[local] PASSED

==== 5 passed in 0.10 seconds ====


# Nagios mode
$ testinfra -qq --nagios test_redis.py
TESTINFRA OK - 5 passed, 0 failed, 0 skipped in 0.12 seconds

We’ve seen the success case so let’s see the failure mode.

$ sudo systemctl stop redis

$ testinfra -v test_redis.py
... snip ...
test_redis.py::test_socket_listening[local] FAILED

Socket = <socket>

    def test_socket_listening(Socket):
        socket = Socket('tcp://127.0.0.1:6379')
>       assert socket.is_listening
E       assert <socket tcp://127.0.0.1:6379>.is_listening

test_redis.py:19: AssertionError
... snip ...


# Nagios mode
$ testinfra --nagios test_redis.py
TESTINFRA CRITICAL - 2 passed, 3 failed, 0 skipped in 0.13 seconds
... snip ...

I experimented with testinfra while working with a small team of mostly junior python devs who had no experience of other programming languages (beyond a little bash). They wanted to write their own system level tests and we picked testinfra due to its tiny learning curve and very small ramp up cost. Due to their existing python skills they were productive in the tool in the first hours of use.