Skip to content

Commit 665b96c

Browse files
committed
Proof-of-concept support for managing /etc/hosts.
Hooks into the up and destroy commands to manage changes to the /etc/hosts file on active machines.
0 parents  commit 665b96c

15 files changed

+303
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.gem
2+
pkg
3+
Gemfile.lock
4+
test/.vagrant

Gemfile

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
source 'https://rubygems.org'
2+
3+
gemspec
4+
5+
group :development do
6+
gem 'vagrant', github: 'mitchellh/vagrant', tag: 'v1.1.2'
7+
end

LICENSE.txt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2013 Shawn Dahlen
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Vagrant Host Manager
2+
====================
3+
`vagrant-hostmanager` is a Vagrant 1.1+ plugin that manages the `/etc/hosts`
4+
file on guest machines. Its goal is to enable resolution of multi-machine
5+
environments deployed with a cloud provider where IP addresses are not known
6+
in advance.
7+
8+
Status
9+
------
10+
The current implementation is a proof-of-concept supporting the larger
11+
objective of using Vagrant as a cloud management interface for development
12+
and production environments.
13+
14+
The plugin has been tested with Vagrant 1.1.4.
15+
16+
Installation
17+
------------
18+
Install the plugin following the typical Vagrant 1.1 procedure:
19+
20+
vagrant plugin install vagrant-hostmanager
21+
22+
Usage
23+
-----
24+
The plugin hooks into the `vagrant up` and `vagrant destroy` commands
25+
automatically updating the `/etc/hosts` file on each active machine that
26+
is using the same provider.
27+
28+
A machine's IP address is defined by either the static IP for a private
29+
network configuration or by the SSH host configuration.
30+
31+
A machine's host name is defined by `config.vm.hostname`. If this is not
32+
set, it falls back to the symbol defining the machine in the Vagrantfile.
33+
34+
Contribute
35+
----------
36+
Contributions are welcome.
37+
38+
1. Fork it
39+
2. Create your feature branch (`git checkout -b my-new-feature`)
40+
3. Commit your changes (`git commit -am 'Add some feature'`)
41+
4. Push to the branch (`git push origin my-new-feature`)
42+
5. Create new Pull Request

bin/build.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
rm *.gem
2+
gem uninstall -a vagrant-hostmanager
3+
gem build *.gemspec
4+
gem install *.gem
5+
vagrant plugin install vagrant-hostmanager

bin/test.sh

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cd test
2+
vagrant up
3+
echo "[server1] /etc/hosts file:"
4+
vagrant ssh server1 -c 'cat /etc/hosts'
5+
echo "[server2] /etc/hosts file:"
6+
vagrant ssh server2 -c 'cat /etc/hosts'
7+
vagrant destroy server1 -f
8+
echo "[server2] /etc/hosts file:"
9+
vagrant ssh server2 -c 'cat /etc/hosts'
10+
vagrant destroy server2 -f
11+
cd ..
12+

lib/vagrant-hostmanager.rb

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require 'vagrant'
2+
require 'vagrant-hostmanager/plugin'
3+
require 'vagrant-hostmanager/version'
4+
require 'vagrant-hostmanager/errors'
5+
6+
module VagrantPlugins
7+
module HostManager
8+
def self.source_root
9+
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
10+
end
11+
end
12+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
module VagrantPlugins
2+
module HostManager
3+
module Action
4+
class UpdateHostsFile
5+
def initialize(app, env)
6+
@app, @env = app, env
7+
@translator = Helpers::Translator.new('action.update_hosts_file')
8+
@logger =
9+
Log4r::Logger.new('vagrant_hostmanager::action::update')
10+
end
11+
12+
def call(env)
13+
global_env = env[:machine].env
14+
current_provider = env[:machine].provider_name
15+
16+
# build a list of host entries based on active machines that
17+
# are using the same provider as the current one
18+
matching_machines = []
19+
entries = {}
20+
entries['127.0.0.1'] = 'localhost'
21+
global_env.active_machines.each do |name, provider|
22+
if provider == current_provider
23+
machine = global_env.machine(name, provider)
24+
host = machine.config.vm.hostname || name
25+
entries[get_ip_address(machine)] = host
26+
matching_machines << machine
27+
end
28+
end
29+
30+
# generate hosts file
31+
path = env[:tmp_path].join('hosts')
32+
File.open(path, 'w') do |file|
33+
entries.each_pair do |ip, host|
34+
@logger.info "Adding /etc/hosts entry: #{ip} #{host}"
35+
file << "#{ip}\t#{host}\n"
36+
end
37+
end
38+
39+
# copy the hosts file to each matching machine
40+
# TODO append hostname to loopback address
41+
matching_machines.each do |machine|
42+
env[:ui].info @translator.t('update', { :name => machine.name })
43+
machine.communicate.upload(path, '/tmp/hosts')
44+
machine.communicate.sudo("mv /tmp/hosts /etc/hosts")
45+
end
46+
47+
@app.call(env)
48+
end
49+
50+
protected
51+
52+
def get_ip_address(machine)
53+
ip = nil
54+
machine.config.vm.networks.each do |network|
55+
key, options = network[0], network[1]
56+
ip = options[:ip] if key == :private_network
57+
next if ip
58+
end
59+
60+
ip || machine.ssh_info[:host]
61+
end
62+
end
63+
end
64+
end
65+
end

lib/vagrant-hostmanager/errors.rb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module VagrantPlugins
2+
module HostManager
3+
module Errors
4+
end
5+
end
6+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module VagrantPlugins
2+
module HostManager
3+
module Helpers
4+
class Translator
5+
def self.plugin_namespace=(val)
6+
@@plugin_namespace = val
7+
end
8+
9+
def initialize(namespace)
10+
@namespace = namespace
11+
end
12+
13+
def t(keys, opts = {})
14+
value = I18n.t("#{@@plugin_namespace}.#{@namespace}.#{keys}", opts)
15+
opts[:progress] == false ? value : value + "..."
16+
end
17+
end
18+
end
19+
end
20+
end

lib/vagrant-hostmanager/plugin.rb

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
require 'vagrant-hostmanager/helpers/translator'
2+
require 'vagrant-hostmanager/action/update_hosts_file'
3+
4+
module VagrantPlugins
5+
module HostManager
6+
class Plugin < Vagrant.plugin('2')
7+
name 'HostManager'
8+
description <<-DESC
9+
This plugin manages the /etc/hosts file for guest machines. A entry is
10+
created for each active machine using the hostname attribute.
11+
DESC
12+
13+
action_hook(:hostmanager_up, :machine_action_up) do |hook|
14+
setup_i18n
15+
setup_logging
16+
17+
# TODO use hook.append when defect is fixed within vagrant
18+
hook.after(ProviderVirtualBox::Action::Boot, Action::UpdateHostsFile)
19+
end
20+
21+
action_hook(:hostmanger_destroy, :machine_action_destroy) do |hook|
22+
setup_i18n
23+
setup_logging
24+
25+
# TODO use hook.append when defect is fixed within vagrant
26+
hook.after(
27+
ProviderVirtualBox::Action::DestroyUnusedNetworkInterfaces,
28+
Action::UpdateHostsFile)
29+
end
30+
31+
def self.setup_i18n
32+
I18n.load_path << File.expand_path('locales/en.yml', HostManager.source_root)
33+
I18n.reload!
34+
Helpers::Translator.plugin_namespace = 'vagrant_hostmanager'
35+
end
36+
37+
def self.setup_logging
38+
level = nil
39+
begin
40+
level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
41+
rescue NameError
42+
# This means that the logging constant wasn't found,
43+
# which is fine. We just keep `level` as `nil`. But
44+
# we tell the user.
45+
level = nil
46+
end
47+
48+
# Some constants, such as "true" resolve to booleans, so the
49+
# above error checking doesn't catch it. This will check to make
50+
# sure that the log level is an integer, as Log4r requires.
51+
level = nil if !level.is_a?(Integer)
52+
53+
# Set the logging level on all "vagrant" namespaced
54+
# logs as long as we have a valid level.
55+
if level
56+
logger = Log4r::Logger.new("vagrant_hostmanager")
57+
logger.outputters = Log4r::Outputter.stderr
58+
logger.level = level
59+
logger = nil
60+
end
61+
end
62+
end
63+
end
64+
end

lib/vagrant-hostmanager/version.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module VagrantPlugins
2+
module HostManager
3+
VERSION = '0.0.1'
4+
end
5+
end

locales/en.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
en:
2+
vagrant_hostmanager:
3+
action:
4+
update_hosts_file:
5+
update: "Updating /etc/hosts file on %{name}"

test/Vagrantfile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# -*- mode: ruby -*-
2+
# vi: set ft=ruby :
3+
4+
Vagrant.configure('2') do |config|
5+
config.vm.box = 'precise64-chef11.2'
6+
config.vm.box_url = 'https://opscode-vm.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_chef-11.2.0.box'
7+
8+
config.vm.define :server1 do |server|
9+
server.vm.hostname = 'fry'
10+
server.vm.network :private_network, :ip => '10.0.5.2'
11+
end
12+
13+
config.vm.define :server2 do |server|
14+
server.vm.hostname = 'bender'
15+
server.vm.network :private_network, :ip => '10.0.5.3'
16+
end
17+
end

vagrant-hostmanager.gemspec

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# -*- encoding: utf-8 -*-
2+
lib = File.expand_path('../lib', __FILE__)
3+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4+
require 'vagrant-hostmanager/version'
5+
6+
Gem::Specification.new do |gem|
7+
gem.name = 'vagrant-hostmanager'
8+
gem.version = VagrantPlugins::HostManager::VERSION
9+
gem.authors = ['Shawn Dahlen']
10+
gem.email = ['[email protected]']
11+
gem.description = %q{A Vagrant plugin that manages the /etc/hosts file within a multi-machine environment}
12+
gem.summary = gem.description
13+
14+
gem.files = `git ls-files`.split($/)
15+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16+
gem.require_paths = ['lib']
17+
end

0 commit comments

Comments
 (0)