serverspec への matcher の追加方法に関する覚書。
serverspec のディレクトリ構成は以下のようになっています。
serverspec
├── bin
├── lib
│ └── serverspec
│ ├── backend
│ ├── commands
│ ├── helper
│ └── matchers
└── spec
├── debian
├── gentoo
├── redhat
├── solaris
└── support
matcher の追加では
- lib/serverspec/matcher
- lib/serverspec/commands
- spec
以下を編集する必要があります。
matcher の定義が格納されるディレクトリです。
- be_user matcher -> be_user.rb
- be_group matcher -> be_group.rb
などのように、matcher ごとにファイルが存在し、be_user matcher は be_user.rb で以下のように定義されています。
Spec::Matchers.define :be_user do
match do |actual|
backend.check_user(example, actual)
end
end
matcher から呼び出されるテストを行なうためのコマンドの定義が格納されるディレクトリです。
2013-05-01 現在、以下の 5 つのファイルが存在します。
- base.rb
- 基本となるコマンドの定義
- ここで定義されたコマンドで問題がある OS・ディストリビューションは以下のファイルで個別に対応
- 以下のファイルは base.rb を継承しているので、override する形になる
- debian.rb
- debian の環境依存なコマンドの定義
- gentoo.rb
- gentoo の環境依存なコマンドの定義
- redhat.rb
- redhat の環境依存なコマンドの定義
- solaris.rb
- Solaris の環境依存なコマンドの定義
- Oracle Solaris 11 で動作確認
- Solaris の環境依存なコマンドの定義
be_user matcher から呼び出される check_user は base.rb で以下のように定義されています。
def check_user user
"id #{user}"
end
また、be_installed matcher から呼び出される check_installed など OS やディストリビューションに依存するものは、redhat.rb などでそれぞれ定義され、base.rb では以下のように例外をあげています。
def check_installed package
raise NotImplementedError.new
end
今のところ、have_iptables_rule matcher から呼び出される check_iptables_rule など Linux 固有のものが base.rb に含まれています。これらは、linux.rb を用意してそちらに移動し、Linux ディストリビューションは linux.rb を継承する形になる予定(ゴールデンウィーク中に作業するはず)。
serverspec 自体のテストが格納されるディレクトリです。
各 OS・ディストリビューションごとに以下の 2 つのファイルが存在します。
- matcher_spec.rb
- matcher のテスト
- matcher からテストのためのコマンドを呼び出す際の引数のテスト?
- commands_spec.rb
- matcher から呼び出されるコマンドのテスト
- 生成されたコマンドが正しいかテスト?
matcher を追加する流れは以下のようになります。
- テストを書く
- matcher を定義する
- matcher から呼び出されるコマンドを定義する
以下では、Solaris 用に have_ipfilter_rule matcher を追加した時を例として説明していきます。
have_ipfilter_rule matcher は以下のように使うことを想定しています。
describe 'ipfilter' do
it { should have_ipfilter_rule('pass in quick on lo0 all') }
end
まず、matcher のテストを書きます。
matcher のテストは matcher がテストのためのコマンドを呼び出す際の引数をテストしているようです。
今回は Solaris 用の matcher なので、 spec/solaris/matchers_spec.rb に以下のテストを追加しました。
it_behaves_like 'support have_ipfilter_rule matcher', 'pass in quick on lo0 all'
OS・ディストリビューションに依存しない matcher であれば、全ての spec/*/matchers_spec.rb にテストを書きます。Linux に依存するものであれば spec/{debian,gentoo,redhat}/matchers_spec.rb にテストを書きます。
次に、mathcer から呼び出されるコマンドのテストを書きます。
ここではテストのためのコマンドが正しく生成されているかテストしているようです。
/sbin/ipfstat -io の出力を grep して目的のルールが適用されているかテストするので、 spec/solaris/commands_spec.rb に以下のテストを追加しました。
describe 'check_ipfilter_rule', :os => :solaris do
subject { commands.check_ipfilter_rule('pass in quick on lo0 all') }
it { should eq "/sbin/ipfstat -io 2> /dev/null | grep 'pass in quick on lo0 all'" }
end
have_iptables_rule matcher にならって、matcher から呼び出されるコマンドの生成は check_ipfilter_rule として describe に書いています。
subject で commands.check_ipfilter_rule に引数を渡し、 これが返すものと次の行の should eq "..." に書いたコマンドと一致するかテストしています。
lib/serverspec/matchers/have_ipfilter_rule.rb に以下のように定義しました。
RSpec::Matchers.define :have_ipfilter_rule do |rule|
match do |ipfilter|
backend.check_ipfilter_rule(example, rule)
end
end
1 行目で have_ipfilter_rule matcher を定義しています。 rule には have_ipfilter_rule matcher の引数で、 上で想定した使い方では 'pass in quick on lo0 all' が入ります。
2 行目の ipfilter には describe 部分が入り、 上で想定した使い方では 'ipfilter' が入ります。
3 行目でテストのコマンドを実行します。 ここの example については把握できていません。
matcher の定義ができたら、先に書いたテストを rake spec で実行し、 matcher のテストで FAIL しないことを確認します。
今回追加する have_ipfilter_rule matcher は Solaris 用の matcher なので、 lib/serverspec/commands/base.rb では以下のように例外をあげるようにします。
def check_ipfilter_rule rule
raise NotImplementedError.new
end
lib/serverspec/commands/solaris.rb に以下のように check_ipfliter_rule を定義しました。
def check_ipfilter_rule rule
"/sbin/ipfstat -io 2> /dev/null | grep '#{rule}'"
end
テストのために実行したいコマンドを書くだけなので、特に問題はないと思います。
commands の定義ができたら、先に書いたテストを rake spec で実行し、 commands のテストで FAIL しないことを確認します。
以上で、matcher の追加が完了です。実際に使ってみて問題がないことを確認してください。