Edited at

ChefからInSpecにattributeをJSON経由で渡したら、シンボルが文字列になっていた

More than 1 year has passed since last update.


問題

ChefレシピからattibuteをJSONでテストに渡していたら、symbolを想定していた変数がstringとして設定されていた。

考えてみれば当たり前だが、JSONの書式にはsymbolに相当するものが無い。

JSON.pretty_generateなどでJSONに変換した時点でstringに置き換えられていた。


recipes/default.rb


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メソッドでハッシュを文字列として書き出し、


recipes/default.rb


...

hash = node.to_hash
hash['run_list'] = node['run_list'] # ここだけ読み込み時のエラー回避の為に置き換え
IO.write('/tmp/node.hash', hash.inspect)


テストシナリオではevalで読み込ませれば、うまく行きそうに思えた。。。。が、


test/integration/default/default_test.rb

node = {}

eval('node = ' + `cat /tmp/node.hash`)

...



残念

試してみると、Chefはリモート、InSpecはローカルで実行されていて、InSpecのテストから直接はリモートのファイルを参照できなかった。

shell veriferにして $KITCHEN_USERNAME などを使うか、テストスクリプト中で ssh などを行えばできるのだろうが、、、

その様なことを考えなくて良いjson( ).params によるjson渡しが楽すぎて、シンボルをそのまま渡すことはとりあえず断念。


追記:kitchen exec を用いることで1ノードなら対応できた


test/integration/default/default_test.rb

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 の様子。


test/integration/default/default_test.rb

node = json('/tmp/node.json').params

...


require 'json' も不要


対応3: レシピでノードオブジェクトのvalueとしてsymbol(もしくはsymbolのarray)は使わない様に統一

レシピでactionに渡す値を最初から.to_symや.mapを使って加工すれば良いのでしょうが、。。。本末転倒な感じ。

追記:

そもそもChefだけの場合でもJSONでattributeを渡さなければいけないケースがありうることから、結局はこの対応が必要なのか。

Chefでリソースのactionへの引数を、String(もしくはそのArray)から変換する


参照