ruby コードを Compile フェーズではなく、Convergence フェーズで実行したい場合は、ruby_block リソースを利用することができる。cf. Chefのレシピは上から下に実行されるという誤解
さて、ruby_block リソース中で ruby の実行結果を元に他のリソースを実行したい場合にどのように書けば良いか。
やり方
以下のように書けばできる。
recipes/default.rb
recipe = self
ruby_block "foo" do
block do
contents = File.read("/tmp/foo")
recipe.log "foo" do
message contents
end
end
end
block
ブロックの中では、log
などのリソースメソッドが生えていない(スコープが違う)ので、リソースメソッドを持っているレシピオブジェクトを ローカル変数(ここでは recipe)に格納しておいて呼び出している。
何に使うか
例えば、
- remote_directory で指定ディレクトリ以下のサブディレクトリを全て転送
- remote_directory で転送したサブディレクトリ全てに対して(この情報の取得が Compile フェーズではできない)、/service 以下にシンボリックリンクを貼る
みたいな daemontools でサービス管理している場合にうれしそうな処理ができる。こんな感じ。
recipes/default.rb
recipe = self
remote_directory "/home/sonots/service" do
source "service"
mode "0755"
end
ruby_block "link for daemontools" do
block do
dirs = Dir::glob("/home/sonots/service/*")
dirs.each do |dir|
recipe.link "/service/#{File.basename(dir)}" do
to dir
end
end
end
end
他のやり方
今回のサンプルコードの要件であれば、逆に remote_directory リソースを Compile フェーズで実行させて満たすこともできる。run_action
を使えば Compile フェーズで実行させることができるので、次のように書くこともできそう。でも、自分はやはり Convergence フェーズで実行させたいなぁ。
recipes/default.rb
remote_directory "/home/sonots/service" do
source "service"
mode "0755"
end.run_action(:create)
dirs = Dir::glob("/home/sonots/service/*")
dirs.each do |dir|
link "/service/#{File.basename(dir)}" do
to dir
end
end
おわりに
読みにくくなるので良い子は真似しない方がよいと思う。