koh’s blog

Sys Admin who loves automation

インフラテストツールのTestinfraを試してみた

For English
ServerSpecの代替になりうるテストツールのTestinfraを試してみたメモです。
以前別のテストツールのgossの記事も書いたので合わせて読んでいただければと。
Serverspecに代わるサーバテストツールGossを試してみた

Testinfraとは

https://testinfra.readthedocs.io/en/latest/

About
With Testinfra you can write unit tests in Python to test actual state of your servers configured by management tools like Salt, Ansible, Puppet, Chef and so on.

Testinfra aims to be a Serverspec equivalent in python and is written as a plugin to the powerful Pytest test engine

ざっくりいうとServerSpecのpython版で、Ansibleやchefなどの構成管理ツール実行後のテスト用途に使えるよ。とあります。

特徴

ServerSpecと比較した際に以下のような違いがあります。

豊富なバックエンド

TestinfraではシンプルなSSH以外にもリモートホストへの接続方法があります。
Ansibleのinventoryを共有できたり、docker,kubectlなどコンテナにも対応しています。

https://testinfra.readthedocs.io/en/latest/backends.html

  • local
  • paramiko
  • docker
  • ssh
  • salt
  • ansible
  • kubectl
  • winrm
  • LXC/LXD

上記に対応しています。

対話式での実行

https://testinfra.readthedocs.io/en/latest/api.html

対話式での実行が可能なので、ファイルに書き出すことなくより手軽にテストできます。
ipython等と組み合わせるとより使いやすいです。

[koh@kohs-MBP] ~/vag_test
% ipython
Python 3.7.3 (default, May  1 2019, 16:07:48)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import testinfra

In [2]: host = testinfra.get_host("paramiko://vagrant@Vag1:22", sudo=True)

In [3]: host.file("/etc/passwd").mode == 0o644
Out[3]: True

In [4]:

試してみる

環境

実行元

MacOS Mojave 10.14.5
Python 3.7.3

実行先

CentOS 7.5.1804 on Vagrant
Python 2.7.5

インストール

実行元にpipでインストールします。

$ pip install testinfra

テスト作成

テストファイルを作成します。

test_first.py(クリックで展開)

def test_passwd_file(host):
    passwd = host.file("/etc/passwd")
    assert passwd.contains("root")
    assert passwd.user == "root"
    assert passwd.group == "root"
    assert passwd.mode == 0o644


def test_nginx_running_and_enabled(host):
    nginx = host.service("nginx")
    assert nginx.is_running
    assert nginx.is_enabled


def test_selinux(host):
    cmd = host.run("/usr/sbin/getenforce")
    assert cmd.stdout == "Enforcing\n"

テストの内容としては

となっています。

テスト実施

では実際にテストを実施してみます。
ローカルのMacからVagrant上のCentOSSSHで接続してテストします。
なおVagrantにはnginxをインストールして起動させています。

テスト実施(クリックで展開)

[koh@kohs-MBP] ~/vag_test
% py.test -v test_first.py --connection=ssh --host=Vag1
======================================== test session starts ========================================
platform darwin -- Python 3.7.3, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- /Users/koh/.pyenv/versions/3.7.3/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/koh/vag_test
plugins: xonsh-0.8.12, testinfra-3.0.5
collected 3 items

test_first.py::test_passwd_file[ssh://Vag1] PASSED                                            [ 33%]
test_first.py::test_nginx_running_and_enabled[ssh://Vag1] PASSED                              [ 66%]
test_first.py::test_selinux[ssh://Vag1] PASSED                                                [100%]

===================================== 3 passed in 3.22 seconds ======================================
[koh@kohs-MBP] ~/vag_test
%

結構見やすいですね。
では試しにNginxを停止してテストします。

テスト実施(クリックで展開)

[koh@kohs-MBP] ~/vag_test
% py.test -v test_first.py --connection=ssh --host=Vag1
======================================== test session starts ========================================
platform darwin -- Python 3.7.3, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- /Users/koh/.pyenv/versions/3.7.3/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/koh/vag_test
plugins: xonsh-0.8.12, testinfra-3.0.5
collected 3 items

test_first.py::test_passwd_file[ssh://Vag1] PASSED                                            [ 33%]
test_first.py::test_nginx_running_and_enabled[ssh://Vag1] FAILED                              [ 66%]
test_first.py::test_selinux[ssh://Vag1] PASSED                                                [100%]

============================================= FAILURES ==============================================
____________________________ test_nginx_running_and_enabled[ssh://Vag1] _____________________________

host = <testinfra.host.Host object at 0x103826438>

    def test_nginx_running_and_enabled(host):
        nginx = host.service("nginx")
>       assert nginx.is_running
E       assert False
E        +  where False = <service nginx>.is_running

test_first.py:11: AssertionError
================================ 1 failed, 2 passed in 3.06 seconds =================================
zsh: exit 1     py.test -v test_first.py --connection=ssh --host=Vag1
[koh@kohs-MBP] ~/vag_test
%

どの項目でコケたのか、どのようにコケたのかもわかりやすいです。

複数ホストにも実行可能で、どのホストのどこでコケたのかわかりやすいです。

テスト実施(クリックで展開)

[koh@kohs-MBP] ~/vag_test
% py.test -v test_first.py --connection=ssh --host=Vag1,Vag2
======================================== test session starts ========================================
platform darwin -- Python 3.7.3, pytest-4.4.1, py-1.8.0, pluggy-0.9.0 -- /Users/koh/.pyenv/versions/3.7.3/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/koh/vag_test
plugins: xonsh-0.8.12, testinfra-3.0.5
collected 6 items

test_first.py::test_passwd_file[ssh://Vag1] PASSED                                            [ 16%]
test_first.py::test_nginx_running_and_enabled[ssh://Vag1] PASSED                              [ 33%]
test_first.py::test_selinux[ssh://Vag1] PASSED                                                [ 50%]
test_first.py::test_passwd_file[ssh://Vag2] PASSED                                            [ 66%]
test_first.py::test_nginx_running_and_enabled[ssh://Vag2] FAILED                              [ 83%]
test_first.py::test_selinux[ssh://Vag2] FAILED                                                [100%]

============================================= FAILURES ==============================================
____________________________ test_nginx_running_and_enabled[ssh://Vag2] _____________________________

host = <testinfra.host.Host object at 0x11026f4e0>

    def test_nginx_running_and_enabled(host):
        nginx = host.service("nginx")
>       assert nginx.is_running
E       assert False
E        +  where False = <service nginx>.is_running

test_first.py:11: AssertionError
_____________________________________ test_selinux[ssh://Vag2] ______________________________________

host = <testinfra.host.Host object at 0x11026f4e0>

    def test_selinux(host):
        cmd = host.run("/usr/sbin/getenforce")
>       assert cmd.stdout == "Enforcing\n"
E       AssertionError: assert 'Permissive\n' == 'Enforcing\n'
E         - Permissive
E         + Enforcing

test_first.py:17: AssertionError
================================ 2 failed, 4 passed in 6.14 seconds =================================
zsh: exit 1     py.test -v test_first.py --connection=ssh --host=Vag1,Vag2
[koh@kohs-MBP] ~/vag_test
%

まとめ

やはりServerSpecと比較すると使用できるmodule(resource)の数であったり、ネット上のドキュメントの量(特に日本語)に関してはServerSpecのほうが優れています。
ただ上記の通りTestinfraのほうが優れてる点も多いため、python派の方はTestinfraも検討する価値はあるのではないでしょうか。