右往左往ブログ

日々よりみち

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の範疇になるかもしれません。