vagrant-hostmanager 1.2.3 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -72,6 +72,41 @@ Use:
72
72
  config.vm.provision :hostmanager
73
73
  ```
74
74
 
75
+ Custom IP resolver
76
+ ------------------
77
+
78
+ You can customize way, how host manager resolves IP address
79
+ for each machine. This might be handy in case of aws provider,
80
+ where host name is stored in ssh_info hash of each machine.
81
+ This causes generation of invalid /etc/hosts file.
82
+
83
+ Custom IP resolver gives you oportunity to calculate IP address
84
+ for each machine by yourself. For example:
85
+
86
+ ```ruby
87
+ config.hostmanager.ip_resolver = proc do |vm|
88
+ if hostname = (vm.ssh_info && vm.ssh_info[:host])
89
+ `host #{hostname}`.split("\n").last[/(\d+\.\d+\.\d+\.\d+)/, 1]
90
+ end
91
+ end
92
+ ```
93
+
94
+ Windows support
95
+ ---------------
96
+
97
+ Hostmanager will detect Windows guests and hosts and use the appropriate
98
+ path for the ```hosts``` file: ```%WINDIR%\System32\drivers\etc\hosts```
99
+
100
+ By default on a Windows host, the ```hosts``` file is not writable without
101
+ elevated privileges. If hostmanager detects that it cannot overwrite the file,
102
+ it will attempt to do so with elevated privileges, causing the
103
+ [UAC](http://en.wikipedia.org/wiki/User_Account_Control) prompt to appear.
104
+
105
+ ### UAC limitations
106
+
107
+ Due to limitations caused by UAC, cancelling out of the UAC prompt will not cause any
108
+ visible errors, however the ```hosts``` file will not be updated.
109
+
75
110
  Contribute
76
111
  ----------
77
112
  Contributions are welcome.
@@ -13,7 +13,7 @@ module VagrantPlugins
13
13
 
14
14
  o.on('--provider provider', String,
15
15
  'Update machines with the specific provider.') do |provider|
16
- options[:provider] = provider
16
+ options[:provider] = provider.to_sym
17
17
  end
18
18
  end
19
19
 
@@ -6,6 +6,7 @@ module VagrantPlugins
6
6
  attr_accessor :ignore_private_ip
7
7
  attr_accessor :aliases
8
8
  attr_accessor :include_offline
9
+ attr_accessor :ip_resolver
9
10
 
10
11
  alias_method :enabled?, :enabled
11
12
  alias_method :include_offline?, :include_offline
@@ -17,6 +18,9 @@ module VagrantPlugins
17
18
  @ignore_private_ip = UNSET_VALUE
18
19
  @include_offline = UNSET_VALUE
19
20
  @aliases = []
21
+ @aliases = Array.new
22
+ @include_offline = false
23
+ @ip_resolver = nil
20
24
  end
21
25
 
22
26
  def finalize!
@@ -35,7 +39,8 @@ module VagrantPlugins
35
39
  errors << validate_bool('hostmanager.include_offline', @include_offline)
36
40
  errors.compact!
37
41
 
38
- if !machine.config.hostmanager.aliases.kind_of?(Array) and
42
+ # check if aliases option is an Array
43
+ if !machine.config.hostmanager.aliases.kind_of?(Array) &&
39
44
  !machine.config.hostmanager.aliases.kind_of?(String)
40
45
  errors << I18n.t('vagrant_hostmanager.config.not_an_array_or_string', {
41
46
  :config_key => 'hostmanager.aliases',
@@ -43,7 +48,16 @@ module VagrantPlugins
43
48
  })
44
49
  end
45
50
 
46
- { 'HostManager configuration' => errors }
51
+ if !machine.config.hostmanager.ip_resolver.nil? &&
52
+ !machine.config.hostmanager.ip_resolver.kind_of?(Proc)
53
+ errors << I18n.t('vagrant_hostmanager.config.not_a_proc', {
54
+ :config_key => 'hostmanager.ip_resolver',
55
+ :is_class => ip_resolver.class.to_s,
56
+ })
57
+ end
58
+
59
+ errors.compact!
60
+ { "HostManager configuration" => errors }
47
61
  end
48
62
 
49
63
  private
@@ -8,13 +8,13 @@ module VagrantPlugins
8
8
 
9
9
  if (machine.communicate.test("uname -s | grep SunOS"))
10
10
  realhostfile = '/etc/inet/hosts'
11
- move_cmd = 'mv'
11
+ move_cmd = 'mv'
12
12
  elsif (machine.communicate.test("test -d $Env:SystemRoot"))
13
- realhostfile = 'C:\Windows\System32\Drivers\etc\hosts'
14
- move_cmd = 'mv -force'
13
+ realhostfile = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts"
14
+ move_cmd = 'mv -force'
15
15
  else
16
16
  realhostfile = '/etc/hosts'
17
- move_cmd = 'mv'
17
+ move_cmd = 'mv'
18
18
  end
19
19
  # download and modify file with Vagrant-managed entries
20
20
  file = @global_env.tmp_path.join("hosts.#{machine.name}")
@@ -34,19 +34,23 @@ module VagrantPlugins
34
34
  def update_host
35
35
  # copy and modify hosts file on host with Vagrant-managed entries
36
36
  file = @global_env.tmp_path.join('hosts.local')
37
- # add a "if windows..."
38
- hosts_location = '/etc/hosts'
39
- copy_cmd = 'sudo cp'
40
- # handles the windows hosts file...
41
- if ENV['OS'] == 'Windows_NT'
37
+
38
+ if WindowsSupport.windows?
39
+ # lazily include windows Module
40
+ class << self
41
+ include WindowsSupport unless include? WindowsSupport
42
+ end
43
+
42
44
  hosts_location = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts"
43
- copy_cmd = 'cp'
45
+ copy_proc = Proc.new { windows_copy_file(file, hosts_location) }
46
+ else
47
+ hosts_location = '/etc/hosts'
48
+ copy_proc = Proc.new { `sudo cp #{file} #{hosts_location}` }
44
49
  end
50
+
45
51
  FileUtils.cp(hosts_location, file)
46
52
  update_file(file)
47
-
48
- # copy modified file using sudo for permission
49
- `#{copy_cmd} #{file} #{hosts_location}`
53
+ copy_proc.call
50
54
  end
51
55
 
52
56
  private
@@ -54,6 +58,7 @@ module VagrantPlugins
54
58
  def update_file(file)
55
59
  # build array of host file entries from Vagrant configuration
56
60
  entries = []
61
+ destroyed_entries = []
57
62
  ids = []
58
63
  get_machines.each do |name, p|
59
64
  if @provider == p
@@ -62,8 +67,12 @@ module VagrantPlugins
62
67
  id = machine.id
63
68
  ip = get_ip_address(machine)
64
69
  aliases = machine.config.hostmanager.aliases.join(' ').chomp
65
- entries << "#{ip}\t#{host} #{aliases}\t# VAGRANT ID: #{id}\n"
66
- ids << id unless ids.include?(id)
70
+ if id.nil?
71
+ destroyed_entries << "#{ip}\t#{host} #{aliases}"
72
+ else
73
+ entries << "#{ip}\t#{host} #{aliases}\t# VAGRANT ID: #{id}\n"
74
+ ids << id unless ids.include?(id)
75
+ end
67
76
  end
68
77
  end
69
78
 
@@ -71,6 +80,8 @@ module VagrantPlugins
71
80
  begin
72
81
  # copy each line not managed by Vagrant
73
82
  File.open(file).each_line do |line|
83
+ # Eliminate lines for machines that have been destroyed
84
+ next if destroyed_entries.any? { |entry| line =~ /^#{entry}\t# VAGRANT ID: .*/ }
74
85
  tmp_file << line unless ids.any? { |id| line =~ /# VAGRANT ID: #{id}/ }
75
86
  end
76
87
 
@@ -84,12 +95,17 @@ module VagrantPlugins
84
95
  end
85
96
 
86
97
  def get_ip_address(machine)
87
- ip = nil
88
- if machine.config.hostmanager.ignore_private_ip != true
89
- machine.config.vm.networks.each do |network|
90
- key, options = network[0], network[1]
91
- ip = options[:ip] if key == :private_network
92
- next if ip
98
+ custom_ip_resolver = machine.config.hostmanager.ip_resolver
99
+ if custom_ip_resolver
100
+ custom_ip_resolver.call(machine)
101
+ else
102
+ ip = nil
103
+ if machine.config.hostmanager.ignore_private_ip != true
104
+ machine.config.vm.networks.each do |network|
105
+ key, options = network[0], network[1]
106
+ ip = options[:ip] if key == :private_network
107
+ next if ip
108
+ end
93
109
  end
94
110
  end
95
111
  ip || (machine.ssh_info ? machine.ssh_info[:host] : nil)
@@ -110,6 +126,45 @@ module VagrantPlugins
110
126
  @global_env.active_machines
111
127
  end
112
128
  end
129
+
130
+ ## Windows support for copying files, requesting elevated privileges if necessary
131
+ module WindowsSupport
132
+ require 'rbconfig'
133
+
134
+ def self.windows?
135
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
136
+ end
137
+
138
+ require 'win32ole' if windows?
139
+
140
+ def windows_copy_file(source, dest)
141
+ begin
142
+ # First, try Ruby copy
143
+ FileUtils.cp(source, dest)
144
+ rescue Errno::EACCES
145
+ # Access denied, try with elevated privileges
146
+ windows_copy_file_elevated(source, dest)
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def windows_copy_file_elevated(source, dest)
153
+ # copy command only supports backslashes as separators
154
+ source, dest = [source, dest].map { |s| s.to_s.gsub(/\//, '\\') }
155
+
156
+ # run 'cmd /C copy ...' with elevated privilege, minimized
157
+ copy_cmd = "copy \"#{source}\" \"#{dest}\""
158
+ WIN32OLE.new('Shell.Application').ShellExecute('cmd', "/C #{copy_cmd}", nil, 'runas', 7)
159
+
160
+ # Unfortunately, ShellExecute does not give us a status code,
161
+ # and it is non-blocking so we can't reliably compare the file contents
162
+ # to see if they were copied.
163
+ #
164
+ # If the user rejects the UAC prompt, vagrant will silently continue
165
+ # without updating the hostsfile.
166
+ end
167
+ end
113
168
  end
114
169
  end
115
170
  end
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module HostManager
3
- VERSION = '1.2.3'
3
+ VERSION = '1.3.0'
4
4
  end
5
5
  end
@@ -7,3 +7,4 @@ en:
7
7
  config:
8
8
  not_a_bool: "A value for %{config_key} can only be true or false, not type '%{value}'"
9
9
  not_an_array_or_string: "A value for %{config_key} must be an Array or String, not type '%{is_class}'"
10
+ not_a_proc: "A value for %{config_key} must be a Proc, not type '%{is_class}'"
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
11
11
  gem.email = ['shawn@dahlen.me']
12
12
  gem.description = %q{A Vagrant plugin that manages the /etc/hosts file within a multi-machine environment}
13
13
  gem.summary = gem.description
14
+ gem.license = 'MIT'
14
15
 
15
16
  gem.files = `git ls-files`.split($/)
16
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-hostmanager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-04 00:00:00.000000000 Z
12
+ date: 2013-12-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -73,7 +73,8 @@ files:
73
73
  - test/test.sh
74
74
  - vagrant-hostmanager.gemspec
75
75
  homepage:
76
- licenses: []
76
+ licenses:
77
+ - MIT
77
78
  post_install_message:
78
79
  rdoc_options: []
79
80
  require_paths: