インフラの自動テストツール「Infrataster」を試す
今回はインフラに対する自動テストツールであるInfratasterを軽く試してみたいと思います。
Infratasterはインフラの外部から振る舞いをテストするツールで、構築したサーバに対してそのサーバのサービス利用者に近い視点でテストをすることができるのが特徴です。
ちなみにインフラの自動テストと言えばServerspecがよく知られていますが、サーバ内部からテストするServerspecに対してInfratasterは外側からと、ひと味違ったアプローチのテストツールとなっています。
準備
Infratasterの公式サイトにチュートリアルがありますが、今回は最初の一歩としてそれをさらに単純化したものを試していきます。
ローカルのマシンに作業ディレクトリを作り、その中でInfratasterを動かしていきましょう。 MacもしくはLinuxなどで試してみてください。
$ mkdir infrataster-sample $ cd infrataster-sample
テスト対象のサーバーはVagrantでローカルに作成することとします。
なおこのチュートリアルにはRubyとbundler、Vagrantが必要ですが、それらの導入に関しては省略します。また、Rubyのバージョンは2.1.5を用いて動作を検証しています。
Infratasterを使ったテストの準備
まずローカルマシンにInfratasterを導入しテストを実行する準備を行います。
Infratasterのインストール
bundlerで使ってインストールすることとします。
以下の内容でGemfileを作成してください。
Gemfile
source 'https://rubygems.org' gem 'infrataster'
Gemfileを作成できたらbundle installでインストールします。
$ bundle install
RSpecの設定
InfratasterはRSpecの上で動きますのでRSpecを実行する準備が必要です。
RSpecの設定ファイルとspec_helper.rbを作成するために以下のコマンドを実行します。
$ rspec --init
このコマンドによって以下のふたつのファイルが生成されます。
- .rspec
- spec/spec_helper.rb
.rspec
.rspecは生成されたファイルそのままで問題ありません。
--color --require spec_helper
spec/spec_helper.rb
spec_helper.rbは生成されたファイルの冒頭に以下を追加したものとなります。
require 'infrataster/rspec' Infrataster::Server.define( :app, '192.168.0.0/16', vagrant: true, )
テスト対象としてapp
という名前のサーバーを定義しています。Vagrantを使う場合IPアドレスはネットマスクで書くことができます。
コメント行を全て削除すると今回の例では以下の通りのファイル内容となります。
require 'infrataster/rspec' Infrataster::Server.define( :app, '192.168.0.0/16', vagrant: true, ) RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end end
テストケースの作成
spec_helper.rbで定義したapp
に対してhttpdのテストを作成します。
spec/example_spec.rb
require 'spec_helper' describe server(:app) do describe http('http://app') do it "responds content including 'Hello Apache'" do expect(response.body).to include('Hello Apache') end it "responds as 'text/html; charset=UTF-8'" do expect(response.headers['content-type']).to eq("text/html; charset=UTF-8") end end end
これでテストの方は準備ができました。
テスト対象のサーバーの準備
続いてテスト対象のサーバーをセットアップしていきます。
以下の内容でVagrantfileを作成してください。
Vagrantfile
VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "hansode/centos-7.1.1503-x86_64" config.vm.define :app do |c| c.vm.network "private_network", ip: "192.168.33.10" end end
今回BOXはhansode/centos-7.1.1503-x86_64を使います。
実行
とりあえずこの時点でvagrant upしてテストを実行してみましょう。
$ vagrant up
無事仮想マシンが立ち上がったらローカルでrspecコマンドを実行しテストを走らせます。
$ rspec FF Failures: 1) server 'app' http 'http://app' with {:params=>{}, :method=>:get, :headers=>{}} responds content including 'Hello Apache' Failure/Error: expect(response.body).to include('Hello Apache') Faraday::ConnectionFailed: Connection refused - connect(2) for "192.168.33.10" port 80 # ./spec/example_spec.rb:8:in `block (3 levels) in <top (required)>' 2) server 'app' http 'http://app' with {:params=>{}, :method=>:get, :headers=>{}} responds as 'text/html' Failure/Error: expect(response.headers['content-type']).to eq("text/html") Faraday::ConnectionFailed: Connection refused - connect(2) for "192.168.33.10" port 80 # ./spec/example_spec.rb:11:in `block (3 levels) in <top (required)>' Finished in 0.01418 seconds (files took 2.53 seconds to load) 2 examples, 2 failures
当然ながら失敗します。まだサーバーのセットアップを行っていないので正しい結果です。そしてこれによりテストがきちんと動いていることを確認できました。
これはテストを先に書く、TDDで言うところのテストファーストになっています。実際は必ずしもテストファーストにこだわる必要はないと思われますが、テストがきちんと働いていることを確認するためにわざと失敗させるステップは踏んでおいた方がいいでしょう。
httpdのインストール
昨今はこれくらいの検証にもプロビジョナーを使ったセットアップをしたいところですが、チュートリアル的にはファイルが多くなり分かりにくくなるので、Infratasterにフォーカスするために今回はあえて手動でhttpdのインストールします。
仮想マシン上で以下のようにインストールして起動してください。
$ sudo yum install httpd $ sudo chkconfig httpd on $ sudo service httpd start
今回は実験ですので、起動時にServerNameに関するエラーメッセージが出ますが無視します。実際の運用では無視しないでください。
コンテンツの準備
実際にコンテンツが返ってくることをテストしますので、コンテンツの準備も行います。
/var/www/html/index.html
以下の内容で作成してください。
Hello Apache
再度テストの実行
テスト対象のサーバーの準備ができたので再度テストを実行します。
$ rspec FF Failures: 1) server 'app' http 'http://app' with {:params=>{}, :method=>:get, :headers=>{}} responds content including 'Hello Apache' Failure/Error: expect(response.body).to include('Hello Apache') Faraday::ConnectionFailed: Connection refused - connect(2) for "192.168.33.10" port 80 # ./spec/example_spec.rb:8:in `block (3 levels) in <top (required)>' 2) server 'app' http 'http://app' with {:params=>{}, :method=>:get, :headers=>{}} responds as 'text/html' Failure/Error: expect(response.headers['content-type']).to eq("text/html") Faraday::ConnectionFailed: Connection refused - connect(2) for "192.168.33.10" port 80 # ./spec/example_spec.rb:11:in `block (3 levels) in <top (required)>' Finished in 0.01194 seconds (files took 2.13 seconds to load) 2 examples, 2 failures
また失敗してしまいました。エラーの原因は、
Connection refused - connect(2) for "192.168.33.10" port 80
とのことなので80番ポートへの接続を拒否されているようです。
仮想マシンのiptablesの設定確認
サーバー設定を確認してみるとiptablesの設定を忘れていました。CentOS 7からfirewalldが使われるようになっていますので、まずiptablesに切り替えます。
$ sudo systemctl stop firewalld $ sudo systemctl mask firewalld $ sudo yum install iptables-services $ sudo systemctl enable iptables $ sudo service iptables save
/etc/sysconfig/iptables
# Firewall configuration written by system-config-firewall # Manual customization of this file is not recommended. *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT
ここに以下の行を追加して
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
ルールをリロードします。
$ sudo service iptables reload
再度テストを実行
さあこれでどうでしょうか。再度rspecを実行します。
$ rspec .. Finished in 0.01547 seconds (files took 2.12 seconds to load) 2 examples, 0 failures
今度は無事通りました。これでrspecを使ったサーバ検証ができるようになりました。
終わりに
いかがだったでしょうか?
Infratasterは実際のクライアントの動きを模して外部からテストするため、結合テストにもってこいのツールではないかと思います。
冒頭で少し述べたように、同じインフラのテストツールであるServerspecがインフラの内部からテストするものなのに対し、Infratasterはインフラの外部から振る舞いをテストするツールであるという違いがあります。
つまりServerspecによるテストはホワイトボックステスト、Infratasterによるテストはブラックボックステストをするものと言えます。
従ってこのふたつのツールは競合するものではなく補完関係にあるもので、両者を併用することでより堅牢なインフラを構築することができるでしょう。
またInfratasterは内部にアクセスできないサーバーに対してもテストが可能であるという利点もあり、Serverspecではカバーできないケースでも対応が可能になるという一面もあります。
今回のようにウェブサーバの動作をテストするケースではCapybaraやSeleniumのようなエンドツーエンドテストツールを使うという手もありますが、本格的なそれらのツールは学習コストやセットアップの手間も相応にかかりますので、インフラサイドからシンプルなテストを書く分にはInfratasterに分がありそうです。
なお現在Infratasterでテストに利用できるリソースとして以下のものがあります。
- http
- capybara
- mysql_query(プラグインとして提供)
正直まだあまり充実しているとは言えませんが、今後様々な設定のテストに対応したプラグインが増えてくればさらに価値のあるプロダクトになりそうです。ぜひお試しください。