serverspecでconfigファイルを複数行まとめてテストする
configファイルの記述の正当性をserverspecでテストするときは、ヒアドキュメントを使うといいかも、という話です。
configファイルを普通にテストする
serverspecで、configファイルの記述をテストするとき、普通はits(:content)matcherを使います。
describe file('/etc/httpd/conf/httpd.conf') do its(:content) { should match /ServerName www.example.jp/ } end
が、configの記述が1行では意味をなさず、複数行にわたって「ブロック」として記載されていたとき、昔はits(:content)matcherをひたすら繋げて書いていました。
describe file('/etc/td-agent/td-agent.conf') do its(:content) { should match %r{^[[:blank:]]*<source>} } its(:content) { should match %r{^[[:blank:]]*type monitor_agent} } its(:content) { should match %r{^[[:blank:]]*bind 0.0.0.0} } its(:content) { should match %r{^[[:blank:]]*port 24220} } its(:content) { should match %r{^[[:blank:]]*</source>} } end
例はtd-agent.confのテストです。
この記述のダメなところは、結局行単位で評価しているので、前後関係が全く意味を成さないところです。それぞれの行をconfig内のどこにどういう順番で書こうが、そのconfigが実際には無効な記述だったとしても、serverspecのテストは通ります。
素直にits(:content)をまとめて書く
一応、最初から最後までまとめて1つのmatcherで繋げて書くことはできます。
describe file('/etc/td-agent/td-agent.conf') do its(:content) { should match %r{<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n</source>} } end
この記述の良くないところは、仮にテストがfailedしたときに、どこが間違っているのかわかりづらいところです。
2) File "/etc/td-agent/td-agent.conf" content should match /<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n<\/source>/
On host `localhost'
Failure/Error: its(:content) { should match %r{<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n</source>} }
expected "<match debug.**>\n type stdout\n</match>\n\n<source>\n type monitor_agent\n bind 0.0.0.0\n port 24222\n</source>\n\ninclude /etc/td-agent/conf.d/*.conf\n\n" to match /<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n<\/source>/
Diff:
@@ -1,2 +1,12 @@
-/<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n<\/source>/
+<match debug.**>
+ type stdout
+</match>
+
+<source>
+ type monitor_agent
+ bind 0.0.0.0
+ port 24222
+</source>
+
+include /etc/td-agent/conf.d/*.conf
上記はport番号を24222とわざと間違って書いているのですが、ブロック全体が誤っていると捉えられます。というか、いちいちテストコードを書くのも、わざわざ改行を¥nで書かなければいけなかったりと面倒です。
ヒアドキュメントを使う
test-monitor_agent = <<"EOS" <source> type monitor_agent bind 0.0.0.0 port 24220 </source> EOS describe file('/etc/td-agent/td-agent.conf') do its(:content) { should match test-monitor_agent } end
もしテストがfailedしても、その部分を判別してくれます。
1) File "/etc/td-agent/td-agent.conf" content should match "<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n</source>\n"
On host `localhost'
Failure/Error: its(:content) { should match test }
expected "<match debug.**>\n type stdout\n</match>\n\n<source>\n type monitor_agent\n bind 0.0.0.0\n port 24222\n</source>\n\ninclude /etc/td-agent/conf.d/*.conf\n\n" to match "<source>\n type monitor_agent\n bind 0.0.0.0\n port 24220\n</source>\n"
Diff:
@@ -1,6 +1,12 @@
+<match debug.**>
+ type stdout
+</match>
+
<source>
type monitor_agent
bind 0.0.0.0
- port 24220
+ port 24222
</source>
+
+include /etc/td-agent/conf.d/*.conf
普通に書く
普通に書いても動きます。(これ昔はできなかったと思ったのですが…昔から動いてましたっけ?)
describe file('/etc/td-agent/td-agent.conf') do its(:content) { should match " <source> type monitor_agent bind 0.0.0.0 port 24220 </source>" } end
当たり前ですがテストfailed時もヒアドキュメントと同じ表示になります。
一番簡単なのはこれですが、テストコードとconfigの記載が混在するので見づらいという問題はあります。好みですが、自分ならヒアドキュメントで変数化して使うかなという印象です。
そう考えると、configファイルをどこまで厳密にチェックするかは考えものです。
そこまでやるなら、もうconfigファイルの最初から最後までまるっとspecファイルに書いてテストすればいいんじゃないかとか、そもそもそれやるならもうserverspecじゃなくて、正しいconfigとのdiffでいいんじゃないかとか思ったりもします。configファイルのテストはある程度妥協して、あとは正しく動くのかというinfratasterの範疇になるかもしれません。