Vagrantのコアの挙動を変更する方法
Vagrantは素晴らしいプロダクトであることは言うまでもないことですが、時にはVagrant自体の挙動を変更したいと思うこともあります。
たとえば、Vagrantでは作成したインスタンスにChefなどのProvisionerを使ってパッケージをインストールするときに、sudoコマンドを利用してユーザー権限からインストールを実行しますが、そのとき、インスタンス側ではrequiretty (コンソールデバイス必須) の無効化の設定がなされていることが前提になっています。 でも、これ最初からboxをそのように作って入れば良いのですが、そうじゃない場合(virtualbox以外のproviderを使ってクラウドサービス上にインスタンスを立ち上げるとか)は、いちいち自分でその変更を加えたbox(もしくはそれに準ずるもの)を作るのは面倒でかないません。
ということで、今回はVagrantのコアの挙動を、Vagrant本体に手を入れることなく変更する方法を紹介します。
基本的な考え方
Vagrant自体は内部のコマンド自体がプラグインモデルで作られていて、Vagrantは起動時に、Vagrantfileの内容の処理に入る前に、コアのプラグインをロードします。 たとえば export VAGRANT_LOG=info などにした上で、Vagrantfileに exit とだけ記述して起動すると以下のように出力されます。
INFO global: Vagrant version: 1.2.2
INFO manager: Registered plugin: ssh communicator
INFO manager: Registered plugin: kernel
INFO manager: Registered plugin: suspend command
INFO manager: Registered plugin: destroy command
INFO manager: Registered plugin: plugin command
INFO manager: Registered plugin: up command
INFO manager: Registered plugin: init command
INFO manager: Registered plugin: status command
INFO manager: Registered plugin: reload command
INFO manager: Registered plugin: ssh-config command
INFO manager: Registered plugin: provision command
INFO manager: Registered plugin: package command
INFO manager: Registered plugin: halt command
INFO manager: Registered plugin: box command
INFO manager: Registered plugin: resume command
INFO manager: Registered plugin: ssh command
INFO manager: Registered plugin: kernel
INFO manager: Registered plugin: FreeBSD guest
INFO manager: Registered plugin: RedHat guest
INFO manager: Registered plugin: Debian guest
INFO manager: Registered plugin: Fedora guest
INFO manager: Registered plugin: Solaris guest.
INFO manager: Registered plugin: Ubuntu guest
INFO manager: Registered plugin: OpenBSD guest
INFO manager: Registered plugin: Gentoo guest
INFO manager: Registered plugin: Linux guest.
INFO manager: Registered plugin: SUSE guest
INFO manager: Registered plugin: Arch guest
INFO manager: Registered plugin: PLD Linux guest
INFO manager: Registered plugin: VirtualBox provider
INFO manager: Registered plugin: puppet
INFO manager: Registered plugin: CFEngine Provisioner
INFO manager: Registered plugin: chef
INFO manager: Registered plugin: ansible
INFO manager: Registered plugin: shell
INFO manager: Registered plugin: FreeBSD host
INFO manager: Registered plugin: Fedora host
INFO manager: Registered plugin: Gentoo host
INFO manager: Registered plugin: Linux host
INFO manager: Registered plugin: OpenSUSE host
INFO manager: Registered plugin: Arch host
INFO manager: Registered plugin: Windows host
INFO manager: Registered plugin: BSD host
INFO vagrant: `vagrant` invoked: ["up"]
(略)
ここでは、いわゆるユーザープラグイン(saharaとかvagrant-awsみたいなやつ)はロードされず、あくまでコアのみがロードされます。 このコアプラグインの挙動を書き換えるためには、初期化処理終了後に、改変した同名のプラグインを再度ロードしなおせばOKです。
実装
今回はsudoの際にrequirettyを無効にしなくても動作するように挙動を変更します。Vagrantでこの処理を担っているのはVagrantPlugins::CommunicatorSSH::Communicatorです。 まず、Vagrantfileがおいてある場所にファイルをコピーします。mkdir -p patch/plugins/communicators/ssh
cp /opt/vagrant/embedded/gems/gems/vagrant-1.2.2/plugins/communicators/ssh/*.rb patch/plugins/communicators/ssh/
次にmonkey patchしましょう!対象はコピーしたディレクトリにあるcommunicator.rbです。 263行目付近に以下を追加します。requirettyをスキップするコードが追加されています。
# Open the channel so we can execute or command
channel = connection.open_channel do |ch|
# monkey path for avoid requiretty ここから追加
channel.request_pty do |ch, success|
@logger.info("Could not obtain pty") if !success
end
#-- end of monkey patch
さてこのモンキーパッチを読み込む処理をVagrantfileに追加しましょう。 ここから追加
とかかれているところからが追加対象です。
# -*- mode: ruby -*-
# vi: set ft=ruby :
### ここから追加
plugin_load_proc = lambda do |directory|
# We only care about directories
next false if !directory.directory?
# If there is a plugin file in the top-level directory, then load
# that up.
plugin_file = directory.join("plugin.rb")
if plugin_file.file?
puts "[INFO]loading monkey patch: #{plugin_file}"
load(plugin_file)
next true
end
end
Vagrant.source_root.join(File.dirname(__FILE__) + "/patch/plugins/communicators/").children(true).each do |directory|
# Ignore non-directories
next if !directory.directory?
# Load from this directory, and exit if we successfully loaded a plugin
puts directory
next if plugin_load_proc.call(directory)
end
### 追加ここまで
Vagrant.configure("2") do |config|
config.vm.box = "<your_box_name_here>"
config.vm.network :private_network, ip: "192.168.33.33"
config.omnibus.chef_version = "11.4.4"
end
やっていることは、Vagrantのソース探索ディレクトリにモンキーパッチ用のパスを追加した上で、そのパス上に存在するプラグインを上書きロードしているだけです。 これでvagrant upコマンドで起動すれば、requirettyが無効でも大丈夫になります。ただし実行時に画面上にインスタンスの中で発行されるコマンドや標準出力が画面上に表示されることにはなります。
まとめ
以上のように簡単に挙動を書き換えることができます。挙動を書き換えたいと思う機会はそう多くないかもしれませんが、例えば特定箇所のログの出力を増やしたいとか出力を他のサービスに連携したいとかインスタンス上で発行するコマンドを書き換えたい、といった場合には使えるかもしれません。 ただしご利用は自己責任でお願いします。アジャイルコーチングやトレーニングを提供しています
株式会社アトラクタでは、アジャイル開発に取り組むチーム向けのコーチングや、認定スクラムマスター研修などの各種トレーニングを提供しています。ぜひお気軽にご相談ください。
詳細はこちら