Finish support for managing host /etc/hosts file.
This commit moves logic into action middleware and ensures that config validation is executed when the hostmanager command is called.
This commit is contained in:
		
							parent
							
								
									064d9b8658
								
							
						
					
					
						commit
						4ce7cd498e
					
				
							
								
								
									
										31
									
								
								README.md
								
								
								
								
							
							
						
						
									
										31
									
								
								README.md
								
								
								
								
							|  | @ -1,17 +1,9 @@ | |||
| Vagrant Host Manager | ||||
| ==================== | ||||
| `vagrant-hostmanager` is a Vagrant 1.1+ plugin that manages the `/etc/hosts` | ||||
| file on guest machines. Its goal is to enable resolution of multi-machine | ||||
| environments deployed with a cloud provider where IP addresses are not known | ||||
| in advance. | ||||
| 
 | ||||
| Status | ||||
| ------ | ||||
| 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.5. | ||||
| file on guest machines (and optionally the host). Its goal is to enable | ||||
| resolution of multi-machine environments deployed with a cloud provider | ||||
| where IP addresses are not known in advance. | ||||
| 
 | ||||
| Installation | ||||
| ------------ | ||||
|  | @ -32,6 +24,9 @@ machines with the same provider will have their `/etc/hosts` file updated | |||
| accordingly. Set the `hostmanager.enabled` attribute to `true` in the | ||||
| Vagrantfile to activate this behavior. | ||||
| 
 | ||||
| To update the host's `/etc/hosts` file, set the `hostmanager.manage_host` | ||||
| attribute to `true`. | ||||
| 
 | ||||
| A machine's IP address is defined by either the static IP for a private | ||||
| network configuration or by the SSH host configuration. To disable | ||||
| using the private network IP address, set `config.hostmanger.ignore_private_ip` | ||||
|  | @ -40,9 +35,8 @@ to true. | |||
| A machine's host name is defined by `config.vm.hostname`. If this is not | ||||
| set, it falls back to the symbol defining the machine in the Vagrantfile. | ||||
| 
 | ||||
| When using include_offline set to true, only boxes that are up or have a | ||||
| private ip configured will be added to the hosts file. You will receive a | ||||
| warning on skipped boxes. | ||||
| If the `hostmanager.include_offline` attribute is set to `true`, boxes that are | ||||
| up or have a private ip configured will be added to the hosts file. | ||||
| 
 | ||||
| In addition, the `hostmanager.aliases` configuration attribute can be used | ||||
| to provide aliases for your host names. | ||||
|  | @ -52,17 +46,18 @@ Example configuration: | |||
| ```ruby | ||||
| Vagrant.configure("2") do |config| | ||||
|   config.hostmanager.enabled = true | ||||
|   config.hostmanager.manage_host = true | ||||
|   config.hostmanager.ignore_private_ip = false | ||||
|   config.hostmanager.include_offline = true | ||||
|   config.vm.define "example-box" do |node| | ||||
|     node.vm.hostname = "example-box-hostname" | ||||
|     node.vm.network :private_network, ip: "192.168.42.42" | ||||
|   config.vm.define 'example-box' do |node| | ||||
|     node.vm.hostname = 'example-box-hostname' | ||||
|     node.vm.network :private_network, ip: '192.168.42.42' | ||||
|     node.hostmanager.aliases = %w(example-box.localdomain example-box-alias) | ||||
|   end | ||||
| end | ||||
| ``` | ||||
| 
 | ||||
| As a last option, you can also choose hostmanager as a provisioner. | ||||
| As a last option, you can use hostmanager as a provisioner. | ||||
| This allows you to use the provisioning order to ensure that hostmanager | ||||
| runs before or after provisioning. The provisioner will collect hosts from | ||||
| boxes with the same provider as the running box. | ||||
|  |  | |||
|  | @ -0,0 +1,31 @@ | |||
| require 'vagrant-hostmanager/action/update_all' | ||||
| require 'vagrant-hostmanager/action/update_guest' | ||||
| require 'vagrant-hostmanager/action/update_host' | ||||
| 
 | ||||
| module VagrantPlugins | ||||
|   module HostManager | ||||
|     module Action | ||||
|       include Vagrant::Action::Builtin | ||||
| 
 | ||||
|       def self.update_all | ||||
|         Vagrant::Action::Builder.new.tap do |builder| | ||||
|           builder.use ConfigValidate | ||||
|           builder.use UpdateAll | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def self.update_guest | ||||
|         Vagrant::Action::Builder.new.tap do |builder| | ||||
|           builder.use ConfigValidate | ||||
|           builder.use UpdateGuest | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def self.update_host | ||||
|         Vagrant::Action::Builder.new.tap do |builder| | ||||
|           builder.use UpdateHost | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -3,19 +3,22 @@ require 'vagrant-hostmanager/hosts_file' | |||
| module VagrantPlugins | ||||
|   module HostManager | ||||
|     module Action | ||||
|       class UpdateHostsFile | ||||
|       class UpdateAll | ||||
|         include HostsFile | ||||
| 
 | ||||
|         def initialize(app, env) | ||||
|           @app = app | ||||
|           @machine = env[:machine] | ||||
|           @logger = Log4r::Logger.new('vagrant::hostmanager::update_hosts_file') | ||||
|           @global_env = @machine.env | ||||
|           @provider = @machine.provider_name | ||||
|           @logger = Log4r::Logger.new('vagrant::hostmanager::update_all') | ||||
|         end | ||||
| 
 | ||||
|         def call(env) | ||||
|           # check if machine is already active | ||||
|           return @app.call(env) if @machine.id | ||||
|           @logger.info 'Continuing update of hosts file for new machine' | ||||
|           # skip if machine is already active on up action | ||||
|           return @app.call(env) if @machine.id && env[:machine_action] == :up | ||||
|           # skip if machine is not active on destroy action | ||||
|           return @app.call(env) if !@machine.id && env[:machine_action] == :destroy | ||||
| 
 | ||||
|           # check config to see if the hosts file should be update automatically | ||||
|           return @app.call(env) unless @machine.config.hostmanager.enabled? | ||||
|  | @ -25,12 +28,17 @@ module VagrantPlugins | |||
| 
 | ||||
|           # update /etc/hosts file on active machines | ||||
|           env[:ui].info I18n.t('vagrant_hostmanager.action.update_guests') | ||||
|           update_guests(@machine.env, @machine.provider_name) | ||||
|           @global_env.active_machines.each do |name, p| | ||||
|             if p == @provider | ||||
|               machine = @global_env.machine(name, p) | ||||
|               update_guest(machine) | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           # update /etc/hosts files on host if enabled | ||||
|           if @machine.config.hostmanager.manage_host? | ||||
|             env[:ui].info I18n.t('vagrant_hostmanager.action.update_host') | ||||
|             update_host(@machine.env, @machine.provider_name) | ||||
|             update_host | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|  | @ -0,0 +1,28 @@ | |||
| require 'vagrant-hostmanager/hosts_file' | ||||
| 
 | ||||
| module VagrantPlugins | ||||
|   module HostManager | ||||
|     module Action | ||||
|       class UpdateGuest | ||||
|         include HostsFile | ||||
| 
 | ||||
|         def initialize(app, env) | ||||
|           @app = app | ||||
|           @machine = env[:machine] | ||||
|           @global_env = @machine.env | ||||
|           @provider = env[:provider] | ||||
|           @logger = Log4r::Logger.new('vagrant::hostmanager::update_guest') | ||||
|         end | ||||
| 
 | ||||
|         def call(env) | ||||
|           env[:ui].info I18n.t('vagrant_hostmanager.action.update_guest', { | ||||
|             :name => @machine.name | ||||
|           }) | ||||
|           update_guest(@machine) | ||||
| 
 | ||||
|           @app.call(env) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,28 @@ | |||
| require 'vagrant-hostmanager/hosts_file' | ||||
| 
 | ||||
| module VagrantPlugins | ||||
|   module HostManager | ||||
|     module Action | ||||
|       class UpdateHost | ||||
|         include HostsFile | ||||
| 
 | ||||
|         def initialize(app, env) | ||||
|           @app = app | ||||
|           @global_env = env[:global_env] | ||||
|           @provider = env[:provider] | ||||
|           @logger = Log4r::Logger.new('vagrant::hostmanager::update_host') | ||||
|         end | ||||
| 
 | ||||
|         def call(env) | ||||
|           if @global_env.config_global.hostmanager.manage_host? | ||||
|             env[:ui].info I18n.t('vagrant_hostmanager.action.update_host') | ||||
|             update_host | ||||
|           end | ||||
| 
 | ||||
|           @app.call(env) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
|  | @ -6,7 +6,7 @@ module VagrantPlugins | |||
|       def execute | ||||
|         options = {} | ||||
|         opts = OptionParser.new do |o| | ||||
|           o.banner = 'Usage: vagrant hostmanager' | ||||
|           o.banner = 'Usage: vagrant hostmanager [vm-name]' | ||||
|           o.separator '' | ||||
|           o.version = VagrantPlugins::HostManager::VERSION | ||||
|           o.program_name = 'vagrant hostmanager' | ||||
|  | @ -17,14 +17,22 @@ module VagrantPlugins | |||
|           end | ||||
|         end | ||||
| 
 | ||||
|         parse_options(opts) | ||||
| 
 | ||||
|         argv = parse_options(opts) | ||||
|         options[:provider] ||= @env.default_provider | ||||
| 
 | ||||
|         update_guests(@env, options[:provider]) | ||||
|         if (@env.config_global.hostmanager.manage_host?) | ||||
|           update_host(@env, options[:provider]) | ||||
|         # update /etc/hosts file for specified guest machines | ||||
|         with_target_vms(argv, options) do |machine| | ||||
|           @env.action_runner.run(Action.update_guest, { | ||||
|             :machine => machine, | ||||
|             :provider => options[:provider] | ||||
|           }) | ||||
|         end | ||||
| 
 | ||||
|         # update /etc/hosts file for host | ||||
|         @env.action_runner.run(Action.update_host, { | ||||
|           :global_env => @env, | ||||
|           :provider => options[:provider] | ||||
|         }) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -3,35 +3,25 @@ require 'tempfile' | |||
| module VagrantPlugins | ||||
|   module HostManager | ||||
|     module HostsFile | ||||
|       def update_guests(env, provider) | ||||
|         entries = get_entries(env, provider) | ||||
| 
 | ||||
|         # update hosts file on each active machine with matching provider | ||||
|         env.active_machines.each do |name, p| | ||||
|           if provider == p | ||||
|             target = env.machine(name, p) | ||||
|             next unless target.communicate.ready? | ||||
|       def update_guest(machine) | ||||
|         return unless machine.communicate.ready? | ||||
| 
 | ||||
|         # download and modify file with Vagrant-managed entries | ||||
|             file = env.tmp_path.join("hosts.#{name}") | ||||
|             target.communicate.download('/etc/hosts', file) | ||||
|             update_file(file, entries, env.tmp_path) | ||||
|         file = @global_env.tmp_path.join("hosts.#{machine.name}") | ||||
|         machine.communicate.download('/etc/hosts', file) | ||||
|         update_file(file) | ||||
| 
 | ||||
|         # upload modified file and remove temporary file | ||||
|             target.communicate.upload(file, '/tmp/hosts') | ||||
|             target.communicate.sudo('mv /tmp/hosts /etc/hosts') | ||||
|         machine.communicate.upload(file, '/tmp/hosts') | ||||
|         machine.communicate.sudo('mv /tmp/hosts /etc/hosts') | ||||
|         FileUtils.rm(file) | ||||
|       end | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def update_host(env, provider) | ||||
|         entries = get_entries(env, provider) | ||||
| 
 | ||||
|       def update_host | ||||
|         # copy and modify hosts file on host with Vagrant-managed entries | ||||
|         file = env.tmp_path.join('hosts.local') | ||||
|         file = @global_env.tmp_path.join('hosts.local') | ||||
|         FileUtils.cp('/etc/hosts', file) | ||||
|         update_file(file, entries, env.tmp_path) | ||||
|         update_file(file) | ||||
| 
 | ||||
|         # copy modified file using sudo for permission | ||||
|         `sudo cp #{file} /etc/hosts` | ||||
|  | @ -39,8 +29,21 @@ module VagrantPlugins | |||
| 
 | ||||
|       private | ||||
| 
 | ||||
|       def update_file(file, entries, tmp_path) | ||||
|         tmp_file = Tempfile.open('hostmanager', tmp_path, 'a') | ||||
|       def update_file(file) | ||||
|         # build array of host file entries from Vagrant configuration | ||||
|         entries = [] | ||||
|         get_machines.each do |name, p| | ||||
|           if @provider == p | ||||
|             machine = @global_env.machine(name, p) | ||||
|             host = machine.config.vm.hostname || name | ||||
|             id = machine.id | ||||
|             ip = get_ip_address(machine) | ||||
|             aliases = machine.config.hostmanager.aliases.join(' ').chomp | ||||
|             entries <<  "#{ip}\t#{host} #{aliases}\t# VAGRANT ID: #{id}\n" | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         tmp_file = Tempfile.open('hostmanager', @global_env.tmp_path, 'a') | ||||
|         begin | ||||
|           # copy each line not managed by Vagrant | ||||
|           File.open(file).each_line do |line| | ||||
|  | @ -56,22 +59,6 @@ module VagrantPlugins | |||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def get_entries(env, provider) | ||||
|         entries = [] | ||||
|         get_machines(env, provider).each do |name, p| | ||||
|           if provider == p | ||||
|             machine = env.machine(name, p) | ||||
|             host = machine.config.vm.hostname || name | ||||
|             id = machine.id | ||||
|             ip = get_ip_address(machine) | ||||
|             aliases = machine.config.hostmanager.aliases.join(' ').chomp | ||||
|             entries <<  "#{ip}\t#{host} #{aliases}\t# VAGRANT ID: #{id}\n" | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         entries | ||||
|       end | ||||
| 
 | ||||
|       def get_ip_address(machine) | ||||
|         ip = nil | ||||
|         if machine.config.hostmanager.ignore_private_ip != true | ||||
|  | @ -84,20 +71,20 @@ module VagrantPlugins | |||
|         ip || (machine.ssh_info ? machine.ssh_info[:host] : nil) | ||||
|       end | ||||
| 
 | ||||
|       def get_machines(env, provider) | ||||
|       def get_machines | ||||
|         # check if offline machines should be included in host entries | ||||
|         if env.config_global.hostmanager.include_offline? | ||||
|         if @global_env.config_global.hostmanager.include_offline? | ||||
|           machines = [] | ||||
|           env.machine_names.each do |name| | ||||
|           @global_env.machine_names.each do |name| | ||||
|             begin | ||||
|               env.machine(name, provider) | ||||
|               machines << [name, provider] | ||||
|               @global_env.machine(name, @provider) | ||||
|               machines << [name, @provider] | ||||
|             rescue Vagrant::Errors::MachineNotFound | ||||
|             end | ||||
|           end | ||||
|           machines | ||||
|         else | ||||
|           env.active_machines | ||||
|           @global_env.active_machines | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| require 'vagrant-hostmanager/action/update_hosts_file' | ||||
| require 'vagrant-hostmanager/action' | ||||
| 
 | ||||
| module VagrantPlugins | ||||
|   module HostManager | ||||
|  | @ -17,11 +17,11 @@ module VagrantPlugins | |||
|       end | ||||
| 
 | ||||
|       action_hook(:hostmanager, :machine_action_up) do |hook| | ||||
|         hook.prepend(Action::UpdateHostsFile) | ||||
|         hook.prepend(Action.update_all) | ||||
|       end | ||||
| 
 | ||||
|       action_hook(:hostmanager, :machine_action_destroy) do |hook| | ||||
|         hook.append(Action::UpdateHostsFile) | ||||
|         hook.prepend(Action.update_all) | ||||
|       end | ||||
| 
 | ||||
|       provisioner(:hostmanager) do | ||||
|  |  | |||
|  | @ -3,10 +3,16 @@ module VagrantPlugins | |||
|     class Provisioner < Vagrant.plugin('2', :provisioner) | ||||
|       include HostsFile | ||||
| 
 | ||||
|       def initialize(machine, config) | ||||
|         super(machine, config) | ||||
|         @global_env = machine.env | ||||
|         @provider = machine.provider_name | ||||
|       end | ||||
| 
 | ||||
|       def provision | ||||
|         update_guests(@machine.env, @machine.provider_name) | ||||
|         if @machine.env.config_global.hostmanager.manage_host? | ||||
|           update_host(@machine.env, @machine.provider_name) | ||||
|         update_guest(@machine) | ||||
|         if @global_env.config_global.hostmanager.manage_host? | ||||
|           update_host | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ en: | |||
|   vagrant_hostmanager: | ||||
|     action: | ||||
|       update_guests: "Updating /etc/hosts file on active guest machines..." | ||||
|       update_guest: "[%{name}] Updating /etc/hosts file..." | ||||
|       update_host: "Updating /etc/hosts file on host machine (password may be required)..." | ||||
|     config: | ||||
|       not_a_bool: "A value for %{config_key} can only be true or false, not type '%{value}'" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Shawn Dahlen
						Shawn Dahlen