itamaeは軽量でChefライクなサーバ構成管理ツールです。詳しくはこちら。
本稿ではitamaeで独自のresourceを定義する方法を紹介します。
resourceとは
文字通りサーバが持つ各リソースの種類に対応していて、itamaeではレシピにresourceを列挙することによってサーバの状態を定義します。
itamaeにはresource pluginを定義するための機構があり、簡単に独自のresourceを定義することができます。
plugin機構の仕組み
レシピのmethod_missing
でItamae::Plugin::Resource::*
が探される
レシピに
foo "bar" do
# ...
end
と書くと、レシピのmethod_missing
でItamae::Plugin::Resource::Foo
が探されます。
itamaeのレシピが読み込まれた段階でこのFoo resourceがレシピのchildren
に追加されます。
そのため、bundle gem
の自動生成規則に従うと、pluginのgem名はitamae-plugin-resource-foo
にする必要があります。
レシピに追加されたresourceがrunされる
レシピの読み込みが終わると、レシピに追加された各resourceがrunされます。 ★
なので、
resource = Itamae::Plugin::Resource::Foo.new(recipe, "bar", &block)
resource.run(nil, dry_run: options[:dry_run])
が実行されたらサーバの状態に応じた適切なコマンドが走るようにItamae::Plugin::Resource::Foo
を定義すればよいことになります。
resourceに共通して必要なメソッドがItamae::Resource::Base
に定義されているので、新たにresourceを定義するときはこれを継承し、必要な部分をオーバーライドする必要があります。
Itamae::Resource::Base
の仕組み
実際に定義するとなるとitamae/resource/base.rbをちゃんと読む必要がありますが、ここでは概要だけ説明します。
-
#new
でresourceにaction等のattributeがセットされる ★- attributeは、ブロックの内側で使えるメソッドで、これがリソースの状態になる
-
define_attribute
を列挙することで任意のパラメータを追加できる
-
#run
でattributeに応じたコマンドを実行する
従って、以下の3つの変更を行えばよいことになります。
-
define_attribute
の列挙 -
pre_action
,set_current_attributes
,show_differences
のオーバーライド -
action_#{action}
メソッドの作成
作成例: ghq resource
例として、ghqを使ってgoの流儀に従ったリポジトリ配置を行うためのリソースを作成しました。
主にgit resourceとmercurial resourceを参考に作りました。
k0kubun/itamae-plugin-resource-ghq
require "itamae/resource/base"
module Itamae
module Plugin
module Resource
class Ghq < Itamae::Resource::Base
define_attribute :action, default: :create
define_attribute :repository, type: String, default_name: true
def set_current_attributes
ensure_ghq_availability
result = run_command("ghq list | grep '#{attributes.repository}$'", error: false)
current.exist = result.exit_status == 0
end
def action_create(options)
unless current.exist
run_command(["ghq", "get", attributes.repository])
end
end
def action_update(options)
result = run_command(["ghq", "get", "-u", attributes.repository])
unless result.stdout.match(/is up to date/)
updated!
end
end
private
def ensure_ghq_availability
if run_command("which ghq", error: false).exit_status != 0
raise "`ghq` command is not available. Please install ghq."
end
end
end
end
end
end
すると、以下のように使えるようになります。
require "itamae/plugin/resource/ghq"
# ghq get ryotarai/itamae
ghq "ryotarai/itamae"
# ghq get -u serverspec/specinfra
ghq "serverspec/specinfra" do
action :update
end