Implement command to update /etc/hosts files.

This commit disables automatic updates by default instead preferring
the user to execute the hostmanager sub-command. The user can enable
auto updates via configuration.
This commit is contained in:
Shawn Dahlen 2013-04-06 08:59:07 -04:00
parent 5865b1ac27
commit 39c197d7cc
9 changed files with 157 additions and 64 deletions

View File

@ -11,19 +11,31 @@ The current implementation is a proof-of-concept supporting the larger
objective of using Vagrant as a cloud management interface for development
and production environments.
The plugin has been tested with Vagrant 1.1.4.
The plugin has been tested with Vagrant 1.1.5.
Installation
------------
Install the plugin following the typical Vagrant 1.1 procedure:
vagrant plugin install vagrant-hostmanager
$ vagrant plugin install vagrant-hostmanager
Usage
-----
The plugin hooks into the `vagrant up` and `vagrant destroy` commands
automatically updating the `/etc/hosts` file on each active machine that
is using the same provider.
To update the `/etc/hosts` file on each active machine, run the following
command:
$ vagrant hostmanager
The plugin may hook into the `vagrant up` and `vagrant destroy` commands
automatically to update the `/etc/hosts` file on each active machine that
is using the same provider. To enable this, add the following configuration
to your Vagrant file:
```ruby
Vagrant.configure('2') do |config|
config.hostmanager.auto_update = true
end
```
A machine's IP address is defined by either the static IP for a private
network configuration or by the SSH host configuration.

View File

@ -1,66 +1,31 @@
require 'vagrant-hostmanager/hosts_file'
module VagrantPlugins
module HostManager
module Action
class UpdateHostsFile
include HostsFile
def initialize(app, env)
@app, @env = app, env
@app = app
@machine = env[:machine]
@translator = Helpers::Translator.new('action.update_hosts_file')
@logger =
@logger =
Log4r::Logger.new('vagrant_hostmanager::action::update')
end
def call(env)
global_env = env[:machine].env
current_provider = env[:machine].provider_name
# check config to see if the hosts file should updated automatically
if @machine.config.hostmanager.auto_update
# generate temporary hosts file
machines = generate(@machine.env, @machine.provider_name)
# build a list of host entries based on active machines that
# are using the same provider as the current one
matching_machines = []
entries = {}
entries['127.0.0.1'] = 'localhost'
global_env.active_machines.each do |name, provider|
if provider == current_provider
machine = global_env.machine(name, provider)
host = machine.config.vm.hostname || name
entries[get_ip_address(machine)] = host
matching_machines << machine
end
end
# generate hosts file
path = env[:tmp_path].join('hosts')
File.open(path, 'w') do |file|
entries.each_pair do |ip, host|
@logger.info "Adding /etc/hosts entry: #{ip} #{host}"
file << "#{ip}\t#{host}\n"
end
end
# copy the hosts file to each matching machine
# TODO append hostname to loopback address
matching_machines.each do |machine|
if machine.communicate.ready?
env[:ui].info @translator.t('update', { :name => machine.name })
machine.communicate.upload(path, '/tmp/hosts')
machine.communicate.sudo("mv /tmp/hosts /etc/hosts")
end
# update /etc/hosts file on each active machine
machines.each { |machine| update(machine) }
end
@app.call(env)
end
protected
def get_ip_address(machine)
ip = nil
machine.config.vm.networks.each do |network|
key, options = network[0], network[1]
ip = options[:ip] if key == :private_network
next if ip
end
ip || machine.ssh_info[:host]
end
end
end
end

View File

@ -0,0 +1,29 @@
module VagrantPlugins
module HostManager
class Command < Vagrant.plugin('2', :command)
include HostsFile
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = 'Usage: vagrant hostmanager [vm-name]'
o.separator ''
o.on('--provider provider', String,
'Update machines with the specific provider.') do |provider|
options[:provider] = provider
end
end
argv = parse_options(opts)
options[:provider] ||= @env.default_provider
generate(@env, options[:provider])
with_target_vms(argv[1..-1], :provider => options[:provider]) do |machine|
update(machine)
end
end
end
end
end

View File

@ -0,0 +1,20 @@
module VagrantPlugins
module HostManager
class Config < Vagrant.plugin('2', :config)
attr_accessor :auto_update
def initialize
@auto_update = false
end
def validate(machine)
errors = []
if !(!!@auto_update == @auto_update)
errors << 'auto_update must be a boolean'
end
{ 'hostmanager' => errors }
end
end
end
end

View File

@ -0,0 +1,57 @@
module VagrantPlugins
module HostManager
module HostsFile
# Generate a hosts file containing the the active machines
# in the Vagrant environment backed by the specified provider.
# The file is written to the Vagrant temporary path.
def generate(env, provider)
machines = []
# define a lambda for looking up a machine's ip address
get_ip_address = lambda do |machine|
ip = nil
machine.config.vm.networks.each do |network|
key, options = network[0], network[1]
ip = options[:ip] if key == :private_network
next if ip
end
ip || machine.ssh_info[:host]
end
# create the temporary hosts file
path = env.tmp_path.join('hosts')
File.open(path, 'w') do |file|
file << "127.0.0.1\tlocalhost\n"
# add a hosts entry for each active machine matching the provider
env.active_machines.each do |name, p|
if provider == p
machines << machine = env.machine(name, provider)
host = machine.config.vm.hostname || name
ip = get_ip_address.call(machine)
@logger.info "Adding /etc/hosts entry: #{ip} #{host}"
file << "#{ip}\t#{host}\n"
end
end
end
machines
end
# Copy the temporary hosts file to the specified machine overwritting
# the existing /etc/hosts file.
def update(machine)
path = machine.env.tmp_path.join('hosts')
if machine.communicate.ready?
machine.env.ui.info translator.t('update', { :name => machine.name })
machine.communicate.upload(path, '/tmp/hosts')
machine.communicate.sudo("mv /tmp/hosts /etc/hosts")
end
end
def translator
Helpers::Translator.new('hosts_file')
end
end
end
end

View File

@ -10,16 +10,26 @@ module VagrantPlugins
created for each active machine using the hostname attribute.
DESC
action_hook(:hostmanager_up, :machine_action_up) do |hook|
def self.update(hook)
setup_i18n
setup_logging
hook.append(Action::UpdateHostsFile)
end
action_hook(:hostmanger_destroy, :machine_action_destroy) do |hook|
config(:hostmanager) do
require_relative 'config'
Config
end
action_hook(:hostmanager_up, :machine_action_up, &method(:update))
action_hook(:hostmanger_destroy, :machine_action_destroy, &method(:update))
# TODO remove duplication of i18n and logging setup
command(:hostmanager) do
setup_i18n
setup_logging
hook.append(Action::UpdateHostsFile)
require_relative 'command'
Command
end
def self.setup_i18n

View File

@ -1,5 +1,5 @@
module VagrantPlugins
module HostManager
VERSION = '0.0.3'
VERSION = '0.0.4'
end
end

View File

@ -1,5 +1,4 @@
en:
vagrant_hostmanager:
action:
update_hosts_file:
update: "Updating /etc/hosts file on %{name}"
hosts_file:
update: "[%{name}] Updating /etc/hosts file"

View File

@ -6,12 +6,13 @@ vagrant ssh server1 -c 'cat /etc/hosts'
echo "[server2] /etc/hosts file:"
vagrant ssh server2 -c 'cat /etc/hosts'
vagrant halt
vagrant up
vagrant hostmanager
vagrant destroy server1 -f
echo "[server1] /etc/hosts file:"
vagrant ssh server1 -c 'cat /etc/hosts'
echo "[server2] /etc/hosts file:"
vagrant ssh server2 -c 'cat /etc/hosts'
vagrant destroy server2 -f
vagrant destroy -f
cd ..