3/20追記
files/default以下に置いた場合もダメでした。
なので、libraries直下にフラットにファイルを置くのが一番良いと思います。
chefのlibraries ( http://docs.opscode.com/essentials_cookbook_libraries.html )
chefの機能を拡張したり、ちょっとしたhelperメソッドを作りたいときに重宝しますが、ちょっとした罠にハマったので紹介します。
ネット上のサンプルを見ると、どれもlibraries直下にのみファイルを置いて使う例が示されています。
.
├── libraries
│ ├── helper.rb
├── recipes
└── default.rb
これは正しく動きますが、実装が進むに連れてhelper.rbが大きくなってきたので、複数のファイルに分割して以下のようにディレクトリを切ったとします。
.
├── libraries
│ ├── helper.rb
│ └── lib
│ ├── aaa.rb
│ └── bbb.rb
├── recipes
└── default.rb
require_relative './lib/aaa.rb'
require_relative './lib/bbb.rb'
def hogehoge
# do something
end
そしてcookbookをchef serverにアップロードし、他のcookbookから使おうとすると、以下のようなload errorで動かなくなります。
in `require_relative': cannot load such file -- (略)/libraries/lib/aaa.rb (LoadError)
どうやらlibraries以下に掘ったディレクトリはアップロードされないようです。
https://tickets.opscode.com/browse/CHEF-672
chef serverにアップロードしなければ普通に動くので、chef-soloのみを使っている場合は問題ありません。(そのお陰で、アップロードするまで気づかなかったんですが。。。)
解決案は以下の4つ
- libraries直下にフラットにファイルを置く
- メリット: そんなにトリッキーじゃない
- デメリット: chef実行時にlibraries以下のすべてのファイルがrequireされてしまう。require順を制御できない。
- デメリット: libraries以下の整理ができない
- libraries/lib以下をgem化し、chef実行時(compile時)にchef_gemを使ってchefのruby環境にそのgemをインストール、libraries/helper.rbからそれをrequireする
- メリット: libraries/lib以下の実装を隠蔽できる
- デメリット: gemサーバがchef実行時のSPOFになる
- デメリット: gemとcookbookの2つをメンテしなくてはならなくなる
- chef serverにきちんとアップロードされるディレクトリにlibraries/lib以下を移動し、helper.rbからrequire_relativeで無理やりrequireする
- メリット: libraries以下が整理できる
- メリット: SPOFが増えない
- デメリット: chef的に気持ち悪い
- chef serverをやめる
- デメリット: やることが多すぎる
案3.が一番無難かなと思います。
files/default以下はディレクトリを作ってもきちんとアップロードされるようなので、libraries/lib以下をfiles/default/libに移動し、helper.rbのrequire_relativeを修正します。
.
├── libraries
│ └── helper.rb
├── recipes
│ └── default.rb
├── files
│ └── default
│ └── lib
│ ├── aaa.rb
│ └── bbb.rb
require_relative '../files/default/lib/aaa.rb'
require_relative '../files/default/lib/bbb.rb'
def hogehoge
# do something
end
3/20追記
案3が無難と書きましたが、この方法を実際にやってみるとchef serverとchef-clientを使うケースにおいて動かない事があります。
理由はchef-clientを使っている場合、files以下のファイルはrecipeコンパイル後、実際にそのファイルが必要となった時に初めてchef serverからダウンロードされる挙動になるため。(lazy load)
recipeコンパイル時にlibrariesのメソッドを呼び出した場合は、files以下のファイルがダウンロードされる前に呼び出されることになるため、cannot load such fileエラーになってしまいます。
client.rbにChef::Config[:no_lazy_load] = trueと設定すればこのlazy loadをoffにできますが、lazy load off前提のlibrariesになってしまうため、このやり方は断念しました。