Chefとrbenvの冪等性

Raspberry PiからIRKit経由でリモコン操作するという願望があって、iOSアプリというかツールを使って赤外線信号を調べた( IRKit iOS SDKで赤外線キャプチャしてみる )ので、あとはRaspberry PiのウェブサーバからIRKitにコマンド(HTTP)を投げるサイトを作ればいい。

んで。

  • Chef SoloでRaspberry Piセットアップしよう
  • Chef SoloってことはRuby入れたやん、Rubyでつくろ
  • モデルとかビューとか要らんしSinatraでいっか、Unicornで実行しよう
  • Raspberry Piの前にVagrant上(Debian)で環境構築テストしてみるか
  • 最近作った開発用のVagrant環境にRuby整ってへん
  • rbenvいれるレシピ書こう、動いた
  • rbenv installの冪等性が保ててないー

と絶賛彷徨い中。

冪等性は何度実行しても同じ結果を得るみたいなことなんだけど、すでにインストール済みのバージョンに対してrbenv installすると既にインストール済みだよとエラーで終了してVagrantのプロビジョニングが停止してしまう。インストール済みなら何もしないように書かないといかん。

めんどくさいけどゴニョゴニョしないといけないのかと思ってchef-rbenvを読む。

def ruby_installed?
  if Array(new_resource.action).include?(:reinstall)
    false
  else
    ::File.directory?(::File.join(rbenv_root, 'versions', @rubie))
  end
end

なるほど、確かにユーザでインストールしたときに ~/.rbenv/versions にディレクトリできてるな。

とりあえず深く考えずに書いた抜粋。Vagrant以外でもレシピ使いたいから指定したユーザで入れている。

src_pathは/home/user、rbenv_rootには/home/user/.rbenvのようなパスが入っている。rubiesにrbenvでインストールするバージョンの配列。

opts[:rubies].each do |version|
  bash "install_ruby_#{version})" do
    cwd opts[:src_path]
    user opts[:user]
    group opts[:user]
    code <<-EOH
      export RBENV_ROOT="#{opts[:rbenv_root]}"
      export PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH"
      eval "$(rbenv init -)"
      rbenv install #{version}
    EOH
    environment 'HOME' => "/home/#{opts[:user]}"
    not_if { ::Dir.exists? "#{opts[:rbenv_root]}/versions/#{version}" }
  end
end

Raspberry Pi上でNginxを動かしているのでunicornのsocketをバックエンドとして繋いで動かすところまでVangrat上のDebianでいけた。Sinatraのプロジェクトは手付かずだけど、適当なURL開いたらIRKitにhttpリクエスト投げるだけだからあともうちょっと