読者です 読者をやめる 読者になる 読者になる

Vagrant のプラグインで使えるフックポイント

vagrant-aws と Route 53

Vagrant そんな途方も無くすごいかって言うとそういう話じゃないですけど、毎日便利に使わせていただいてます。これまでは VirtualBox か VMware 、遊びで docker だったんですが、こないだ諸事情あって仕事の開発環境をインターネット上に出したくなり、 vagrant-aws プラグインを使いました。そうしたら思っていたより、はるかに便利でした。

もう一個諸事情あって、そこで立ち上がった EC2 のインスタンスの Public IP が、 vagrant up をしたときに勝手に Route 53 の所定のレコードセットに入って欲しいです。ドメインとワイルドカード証明書をボスに買ってもらったので、これで https 開発です。

探したらなぜか無かったので、 vagrant-aws-route53 というのを書きました。

フックポイント

この「なんかのイベントの前や後」に実行するためのフックポイント、結構雑なんですがコード読め的な感じだったので読みました。

原理としては、プラグインのクラスの中で Vagrant::Plugin::V2::Plugin.action_hook に特定の文字列とブロックを渡してフックポイントを定義します。ブロックの引数に、そのフックポイントの Vagrant::Action::Hook インスタンスが渡ってくるので、 #before とか #after とか呼び出して、 "middleware" のスタックにおれさまのクラスを差し込みます。ちなみに "middleware" は #call メソッドに応答するクラスであればなんでもいいはず…です。多分。いや、これ、嘘でした。 #call#initialize にわたってくる application#call してあげて middleware スタックの実行を継続してあげないとだめです。下の場合は vagrant-aws プラグインの RunInstanceStartInstance のそれぞれの後にさしこんでます。

ちなみにフックの名前( :assign_ip_to_route53 ってところ)を渡してる引数、いまどこにも使われてないそうです。

action_hook :assign_ip_to_route53, :machine_action_up do |hook|
  require_relative './action/set_ip'
  hook.after VagrantPlugins::AWS::Action::RunInstance, VagrantPlugins::AwsRoute53::Action::SetIp
  hook.after VagrantPlugins::AWS::Action::StartInstance, VagrantPlugins::AwsRoute53::Action::SetIp
end

次にこれはフックを呼び出すですが、 Vagrant::Environment#hook を呼ぶとその名前で走るようです。フックポイント用のスタックを定義するのは呼び出す側では無くて、フックする方なんですね。

def hook(name, opts=nil)
  @logger.info("Running hook: #{name}")
  opts ||= {}
  opts[:callable] ||= Action::Builder.new
  opts[:runner] ||= action_runner
  opts[:action_name] = name
  opts[:env] = self
  opts.delete(:runner).run(opts.delete(:callable), opts)
end

ちなみに、これを使わず直接 Vagrant::Action::Runner#run してるところがあって、 Vagrant::Machine#action_raw です。

def action_raw(name, callable, extra_env=nil)
  # Run the action with the action runner on the environment
  env = {
    action_name: "machine_action_#{name}".to_sym,
    machine: self,
    machine_action: name,
    ui: @ui,
  }.merge(extra_env || {})
  @env.action_runner.run(callable, env)
end

つまるところこれは Provider のアクションが全部ここにきます。 up とか destory とか。なので上のように :machine_action_up が使えるわけでした。 Provider 次第ですが、この粒度の中でさらに上記のように middleware のスタックの任意の場所に差し込むので、ちょっとしたコマンド追加でやりたいようなことはできるだろということなんではないかと想像します。

ちなみに #hook でスタティックに定義されているのは、 :environment_plugins_loaded :environment_load :environment_unload がありました。