問題
ChefレシピからattibuteをJSONでテストに渡していたら、symbolを想定していた変数がstringとして設定されていた。
考えてみれば当たり前だが、JSONの書式にはsymbolに相当するものが無い。
JSON.pretty_generateなどでJSONに変換した時点でstringに置き換えられていた。
node.default['service']['foo']['action'] = [:start, :enable]
...
require 'json'
IO.write('/tmp/node.json', JSON.pretty_generate(node.to_hash))
対応1: 文字列の前提でテストを作る
シンプルに比較対象をシンボルから文字列にする。。。
まあchefのattributeでsymbolが必要なのはactionとnotifies, subscribes くらいの様な気がするので、主にそこだけ気をつければ良いのだが。。。。
hash['service'].each do |s_name, s_attr|
describe service(s_name) do
it { should be_enabled } if s_attr['action'].include?('enable')
it { should_not be_enabled } if s_attr['action'].include?('disable')
it { should be_running } if s_attr['action'].include?('start')
it { should_not be_running } if s_attr['action'].include?('stop')
end
end
対応2: symbolのまま渡す方法は無いか。。
レシピ側で、.inspectメソッドでハッシュを文字列として書き出し、
...
hash = node.to_hash
hash['run_list'] = node['run_list'] # ここだけ読み込み時のエラー回避の為に置き換え
IO.write('/tmp/node.hash', hash.inspect)
テストシナリオではevalで読み込ませれば、うまく行きそうに思えた。。。。が、
node = {}
eval('node = ' + `cat /tmp/node.hash`)
...
残念
試してみると、Chefはリモート、InSpecはローカルで実行されていて、InSpecのテストから直接はリモートのファイルを参照できなかった。
shell veriferにして $KITCHEN_USERNAME などを使うか、テストスクリプト中で ssh などを行えばできるのだろうが、、、
その様なことを考えなくて良いjson( ).params によるjson渡しが楽すぎて、シンボルをそのまま渡すことはとりあえず断念。
追記:kitchen exec を用いることで1ノードなら対応できた
node = {}
eval('node = ' + ` kitchen exec --command='cat /tmp/node.hash' | tail +2 `)
...
kitchenのインスタンス名は渡せていないので、対象が1システムの場合のみ。
やはりshell veriferで素直にローカルにscpかな。。
実はjson( ).paramsはすごかった
InSpecのテストで、jsonからパラメーターを読み込むのに、何気に使っていたjson( ).paramsは、テスト実行ノードがたとえリモートでも、そこのファイルを読み込んでくれる native inspec resource の様子。
node = json('/tmp/node.json').params
...
require 'json' も不要
対応3: レシピでノードオブジェクトのvalueとしてsymbol(もしくはsymbolのarray)は使わない様に統一
レシピでactionに渡す値を最初から.to_symや.mapを使って加工すれば良いのでしょうが、。。。本末転倒な感じ。
追記:
そもそもChefだけの場合でもJSONでattributeを渡さなければいけないケースがありうることから、結局はこの対応が必要なのか。
Chefでリソースのactionへの引数を、String(もしくはそのArray)から変換する