この記事は最終更新から1年以上経過しています。 気をつけてね。
サーバの構築状況を、手動確認の段取りでチェックするserverspec、使いやすくて評判ですね。
注: このエントリはほぼネタなのであまり参考にしてはいけません。
さて、テスト駆動開発という手法は皆さん既にご存知でしょう。今日は折角テストツールがあるのですからサーバ構築をテスト駆動でやってみましょう。
まずFailするSpecサンプル
こちらはパッケージnginx
がインストールされているかテストするserverspecのサンプルです。
require 'spec_helper'
describe package('nginx') do
it { should be_installed }
end
Failすることを確認
Failからはじまるテスト駆動、serverspec
はテストに使ったコマンドも表示してくれます。
Failures:
1) Package "nginx"
Failure/Error: it { should be_installed }
dpkg -s nginx && ! dpkg -s nginx | grep -E '^Status: .+ not-installed$'
Package `nginx' is not installed and no info is available.
Use dpkg --info (= dpkg-deb --info) to examine archive files,
and dpkg --contents (= dpkg-deb --contents) to list their contents.
expected Package "nginx" to be installed
# ./spec/xxx.xxx.xxx.xxx/nginx_spec.rb:4:in `block (2 levels) in <top (required)>'
Finished in 1.01 seconds
1 examples, 1 failures
目出度くFailしました、ではこれを満たすようにサーバ構築のコードを書きましょう!
Rescueせよ
(重ねて言いますがあまり真面目に受け取らないでください。)
構築には何を使いましょう、chef
?puppet
? いえ今日は趣向をこらして、Failを検知できるRspec(serverspec)自身に直させましょう。
ということでnginx_spec.rb
をこの様に変更です。
require 'spec_helper'
describe package('nginx') do
it {
begin
should be_installed
rescue => e
puts e.message
puts "======= Installing Nginx..."
cmd = Serverspec::Type::Command.new('apt-get update && apt-get install -y nginx')
expect(cmd.return_exit_status?(0)).to be_true
end
}
end
nginxのshould be_installed
が例外を吐いたらrescue
してサーバ状態のつじつまを合わせにいきます。
コマンド実行にもserverspec
のマッチャを使うことで、楽にエラーチェックができますね!
serverspecで完結、テスト駆動
では初めにFailしたのと同じ状態のサーバにserverspec
を実行してみます。
$ rspec -fd
Package "nginx"
expected Package "nginx" to be installed
======= Installing Nginx...
should be true
Finished in 13.42 seconds
1 example, 0 failures
expected Package "nginx" to be installed
と、rescue側に入ってつじつま合わせが走りました。
すんだらもう一度serverspec
を実行してみます。
$ rspec -fd
Package "nginx"
should be installed
Finished in 1.03 seconds
1 example, 0 failures
should be installed #=> true
、できました。まさに冪等な収束。
終わりに
テスト駆動はソフト開発でもしばしば難題と言われますが、インフラはこのくらい緩めな手法でもアリかな。
追記
chef-soloを導入してcommunity cookbookから rabbitmq
のインストール状態を確認し、入ってなければ勝手にインストールするserverspecはこちらです。
テストが通ればただのserverspecなところが肝心ですね。
require 'spec_helper'
describe command('which chef-solo') do
it {
begin
should return_exit_status(0)
rescue => e
puts e.message
puts "======= Installing Omnibus Chef..."
cmd = Serverspec::Type::Command.new('curl -L https://www.opscode.com/chef/install.sh | sudo bash')
expect(cmd.return_exit_status?(0)).to be_true
end
}
end
describe package('git') do
it {
begin
should be_installed
rescue => e
puts e.message
puts "======= installing git..."
cmd = serverspec::type::command.new('apt-get update && apt-get install -y git')
expect(cmd.return_exit_status?(0)).to be_true
end
}
end
describe file('/var/chef/cookbooks/.git') do
it {
begin
should be_directory
rescue => e
puts e.message
puts "======= initializing cookbooks directory..."
cmd = Serverspec::Type::Command.new('mkdir -p /var/chef/cookbooks')
expect(cmd.return_exit_status?(0)).to be_true
cmd = Serverspec::Type::Command.new('git init /var/chef/cookbooks')
expect(cmd.return_exit_status?(0)).to be_true
cmd = Serverspec::Type::Command.new('touch /var/chef/cookbooks/.gitkeep')
expect(cmd.return_exit_status?(0)).to be_true
cmd = Serverspec::Type::Command.new('cd /var/chef/cookbooks && git add . && git commit .gitkeep -m "empty init"')
expect(cmd.return_exit_status?(0)).to be_true
end
}
end
describe package('rabbitmq-server') do
it {
begin
should be_installed
rescue => e
puts e.message
puts "======= installing rabbitmq by chef..."
cmd = Serverspec::Type::Command.new('knife cookbook site install rabbitmq')
expect(cmd.return_exit_status?(0)).to be_true
cmd = Serverspec::Type::Command.new('chef-solo -o "rabbitmq"')
expect(cmd.return_exit_status?(0)).to be_true
end
}
end
くれぐれもserverspec導入の参考にしないで下さい。