serverspec-runnerとは?
インフラの状態をテストするフレームワークserverspecを複数のホストに対し気軽に使えて、わかりやすい結果を表示するツールです。
詳しくは↓こちら
serverspec-runnerを使って複数のホストのテストレポートを作る
serverspec-runner@github.com
独自リソースを使う理由
serverspec-runnerはserverspec.orgのリソースに準拠しています。
serverspecを使い込んでくると、どうしても組み込みのリソースでは足りず、その場合はcommandリソースによるコマンドラインやシェルスクリプトの実行になってしまい、コードの再利用性や可読性が下がってしまうため、独自のリソースを使った方が良いケースが出てくるかと思います。
serverspecのリソース拡張について
serverspecの拡張方法は作者のmizzy氏自らO'Reilly Japanの「Serverspec」にて1章を使って丁寧に詳しく書かれております。
serverspec-runnerでリソース拡張する際もここで書かれている内容の通りコードを記述します。
尚、この技術書にて詳細が書かれておりますのでserverspec自体の拡張に関してはここではあまり触れません。ご興味がある方は一読することをお勧めします!
事前準備
rubyが使用できる環境にてserverspec-runnerをインストールし、テストコードのスケルトンを生成します。
gem install serverspec-runner
テストで使用するディレクトリ/tmp/testroot を指定します。
以降「spec_root」と呼ぶ事にします。
serverspec-runner -r /tmp/testroot
want to create spec-tree to /tmp/testroot? (y/n): y
cd /tmp/testroot
spec_rootディレクトリ
lsコマンドで見ると以下のようなファイル・ディレクトリが生成されています。
- scenario.yml
- ssh_options_default.yml
- spec
- serverspecテストコードの場所
- lib
- serverspec-runner拡張リソースコードの場所
サンプル1: 既存リソースを拡張
実用性やテスト内容の妥当性はとりあえず置いといて、ファイルがテキストファイルかを判別するリソース拡張をしてみます。
module Serverspec::Type
class File < Base
def text?
@runner.check_file_is_text(@name)
end
end
end
テキストファイル判別はfileコマンドの結果をgrepすることにしてみます。
class Specinfra::Command::Base::File < Specinfra::Command::Base
class << self
def check_is_text(file)
"file #{escape(file)} | egrep ' text$'"
end
end
end
拡張部分は以上です。
今度はテストコードのほうから、本家のfileリソースには無いbe_textを使用してみます。
require 'spec_helper'
describe file('/tmp/anyfile') do
it { should be_text }
end
テストを成功させるために/tmp/anyfile を作ります。
echo 'あいうえお' > /tmp/anyfile
それでは結果を試してみましょう。
尚、今回は生成時デフォルトのscenario.ymlによるローカルホストのテストですが、リモートホストに実行するにはこちらを参照して下さい。
serverspec-runner
+---------------------------------------+
|description | result |
+---------------------------------------+
|example@anyhost-01(127.0.0.1) | |
| File "/tmp/anyfile" | |
| | OK |
+---------------------------------------+
今度は失敗するケースを試してみます。
dd if=/dev/zero of=/tmp/anyfile count=10 bs=1
serverspec-runner
+---------------------------------------+
|description | result |
+---------------------------------------+
|example@anyhost-01(127.0.0.1) | |
| File "/tmp/anyfile" | |
| | NG |
+---------------------------------------+
サンプル2: 新たにリソースを定義
今度はmysqlという全く新しいリソースを定義し、レプリケーションが正常にできているかチェックできるようにしてみます。構文は以下のようにしようと思います。
be_replicated.from(<レプリケーションマスターサーバのIPアドレス>)
.with_user(<mysqlログイン用ユーザ>)
.with_password(<mysql用ログインパスワード>)
.with_port(<mysqlログイン用ポート>)
Serverspec::Type及びSpecinfra::Command クラスを定義します。
module Serverspec::Type
class Mysql < Base
def replicated?(master=nil, user=nil, password=nil, port=nil)
@runner.check_mysql_is_replicated(master, user, password, port)
end
end
end
class Specinfra::Command::Linux::Base::Mysql < Specinfra::Command::Base
class << self
def check_is_replicated(master=nil, user=nil, password=nil, port=nil)
opt_user = "--user=#{user} " || ''
opt_password = "--password=#{password} " || ''
opt_port = "--port=#{port} " || ''
cmd = ''
cmd += "echo 'show slave status \\G;' | mysql #{opt_user} #{opt_password} #{opt_port} | "
cmd += "grep -e 'Slave_IO_Running: Yes' -e 'Slave_SQL_Running: Yes' -e 'Master_Host: #{master}' | "
cmd += "wc -l | grep -w 3"
cmd
end
end
end
be_replicatedという新しい構文はRSpecのマッチャに処理されないので、自前で定義します。
RSpec::Matchers.define :be_replicated do
match do |host|
host.replicated?(@master, @user, @password, @port)
end
chain :from do |master|
@master = master
end
chain :with_user do |user|
@user = user
end
chain :with_password do |password|
@password = password
end
chain :with_port do |port|
@port = port
end
end
最後に、serverspecのフレームワークがServerspec::Helper::Typeにてやっていることと同じように、新しいリソースタイプを認識させるようにします。
module Serverspec
module Helper
module Type
types = %w(
mysql
)
types.each {|type| require "extension/serverspec/type/#{type}" }
types.each do |type|
define_method type do |*args|
name = args.first
eval "Serverspec::Type::#{type.to_camel_case}.new(name)"
end
end
end
end
end
以上が拡張部分です。次にテストコードを書きます。
require 'spec_helper'
describe mysql("replication test") do
it { should be_replicated.from('192.168.10.10')
.with_user('repluser')
.with_password('replpasswd')
.with_port('3306') }
end
それでは試してみましょう。
(予め下記ホスト間にてリプリケーションは正常にされているものとします)
serverspec-runner
+---------------------------------------+
|description | result |
+---------------------------------------+
|example@anyhost-01(127.0.0.1) | |
| Mysql "replication test" | |
| | OK |
+---------------------------------------+
最後に
O'Reilly Japanの「Serverspec」とgithub.comのserverspec/specinfraページを見てserverspec-runner側からリソースを拡張する方法を実装してみました。
serverspecそのものの拡張に対しての間違いやもっとスマートな実装がありましたらコメントにでもこっそり教えて頂けると嬉しいです。