vagrant-hosts-provisioner 1.0.1 → 2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -3
- data/lib/vagrant-hosts-provisioner.rb +0 -2
- data/lib/vagrant-hosts-provisioner/action.rb +25 -0
- data/lib/vagrant-hosts-provisioner/action/add.rb +24 -0
- data/lib/vagrant-hosts-provisioner/action/remove.rb +24 -0
- data/lib/vagrant-hosts-provisioner/config.rb +45 -4
- data/lib/vagrant-hosts-provisioner/hosts.rb +264 -0
- data/lib/vagrant-hosts-provisioner/plugin.rb +23 -2
- data/lib/vagrant-hosts-provisioner/provisioner.rb +3 -226
- data/lib/vagrant-hosts-provisioner/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9682f369371d62fb9d52045d0b5349578adb9da
|
4
|
+
data.tar.gz: acabdb3efdbc7691a848fee0bcca1ce86c235adc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62771fda68d297ee2b465f9ae7beaa93a47c15a16f12dd0a504cfcdc23292cc336563d00e60d3314f04acc33537afd27c80cdf96e94cac816370c3e816b3b9b5
|
7
|
+
data.tar.gz: 61875020970a84ba8facc499bc5422b501b01e0129b6daa447776e2b765cbc7344623984206591101231835ae66270e5c2e2c9ba9b116bca37619d515d483dcb
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# VagrantPlugins::HostsProvisioner
|
2
2
|
|
3
|
-
A Vagrant provisioner for managing the /etc/hosts file of the host and guest machines.
|
4
|
-
|
3
|
+
A Vagrant provisioner for managing the /etc/hosts file of the host and guest machines.
|
5
4
|
## Installation
|
6
5
|
|
7
6
|
Install into vagrant's isolated RubyGems instance using:
|
@@ -10,7 +9,22 @@ Install into vagrant's isolated RubyGems instance using:
|
|
10
9
|
|
11
10
|
## Usage
|
12
11
|
|
13
|
-
|
12
|
+
Example configuration:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
config.vm.provision :hostsupdate, run: 'always' do |host|
|
16
|
+
host.hostname = 'demo-hostname'
|
17
|
+
host.manage_guest = true
|
18
|
+
host.manage_host = true
|
19
|
+
host.aliases = [
|
20
|
+
'hostname-aliase1',
|
21
|
+
'hostname-aliase2'
|
22
|
+
]
|
23
|
+
host.files = [
|
24
|
+
'config/hosts.json'
|
25
|
+
]
|
26
|
+
end
|
27
|
+
```
|
14
28
|
|
15
29
|
## Contributing
|
16
30
|
|
@@ -5,8 +5,6 @@ require "vagrant-hosts-provisioner/version"
|
|
5
5
|
module VagrantPlugins
|
6
6
|
module HostsProvisioner
|
7
7
|
# This returns the path to the source of this plugin.
|
8
|
-
#
|
9
|
-
# @return [Pathname]
|
10
8
|
def self.source_root
|
11
9
|
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
12
10
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'action/add'
|
2
|
+
require_relative 'action/remove'
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module HostsProvisioner
|
6
|
+
module Action
|
7
|
+
include Vagrant::Action::Builtin
|
8
|
+
|
9
|
+
def self.add
|
10
|
+
Vagrant::Action::Builder.new.tap do |builder|
|
11
|
+
builder.use ConfigValidate
|
12
|
+
builder.use Add
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.remove
|
17
|
+
Vagrant::Action::Builder.new.tap do |builder|
|
18
|
+
builder.use ConfigValidate
|
19
|
+
builder.use Remove
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module HostsProvisioner
|
3
|
+
module Action
|
4
|
+
class Add
|
5
|
+
|
6
|
+
def initialize(app, env)
|
7
|
+
@app = app
|
8
|
+
@machine = env[:machine]
|
9
|
+
@config = @machine.env.vagrantfile.config
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
@config.vm.provisioners.each do |provisioner|
|
14
|
+
if provisioner.name == :hostsupdate
|
15
|
+
Hosts.new(@machine, provisioner.config).add
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module HostsProvisioner
|
3
|
+
module Action
|
4
|
+
class Remove
|
5
|
+
|
6
|
+
def initialize(app, env)
|
7
|
+
@app = app
|
8
|
+
@machine = env[:machine]
|
9
|
+
@config = @machine.env.vagrantfile.config
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
@config.vm.provisioners.each do |provisioner|
|
14
|
+
if provisioner.name == :hostsupdate
|
15
|
+
Hosts.new(@machine, provisioner.config).remove
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module VagrantPlugins
|
2
2
|
module HostsProvisioner
|
3
3
|
class Config < Vagrant.plugin("2", :config)
|
4
|
+
attr_accessor :id
|
4
5
|
attr_accessor :hostname
|
5
6
|
attr_accessor :manage_guest
|
6
7
|
attr_accessor :manage_host
|
@@ -8,6 +9,7 @@ module VagrantPlugins
|
|
8
9
|
attr_accessor :files
|
9
10
|
|
10
11
|
def initialize
|
12
|
+
@id = UNSET_VALUE
|
11
13
|
@hostname = UNSET_VALUE
|
12
14
|
@manage_guest = UNSET_VALUE
|
13
15
|
@manage_host = UNSET_VALUE
|
@@ -16,6 +18,7 @@ module VagrantPlugins
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def finalize!
|
21
|
+
@id = 0 if @id == UNSET_VALUE
|
19
22
|
@hostname = nil if @hostname == UNSET_VALUE
|
20
23
|
@manage_guest = false if @manage_guest == UNSET_VALUE
|
21
24
|
@manage_host = false if @manage_host == UNSET_VALUE
|
@@ -29,16 +32,18 @@ module VagrantPlugins
|
|
29
32
|
def validate(machine)
|
30
33
|
errors = []
|
31
34
|
|
35
|
+
errors << validate_number('id', id)
|
36
|
+
errors << validate_bool_or_string('hostname', hostname)
|
32
37
|
errors << validate_bool('manage_guest', manage_guest)
|
33
38
|
errors << validate_bool('manage_host', manage_host)
|
34
|
-
errors << validate_array_or_string('aliases', aliases)
|
35
39
|
errors << validate_array_or_string('files', files)
|
40
|
+
errors << validate_array_or_string('aliases', aliases)
|
36
41
|
errors.compact!
|
37
42
|
|
38
43
|
{ "HostsProvisioner configuration" => errors }
|
39
44
|
end
|
40
45
|
|
41
|
-
# Checks if a option is
|
46
|
+
# Checks if a option is Boolean
|
42
47
|
def validate_bool(key, value)
|
43
48
|
if ![TrueClass, FalseClass].include?(value.class) && value != UNSET_VALUE
|
44
49
|
I18n.t('vagrant_hostsprovisioner.error.invalid_bool', {
|
@@ -50,9 +55,21 @@ module VagrantPlugins
|
|
50
55
|
end
|
51
56
|
end
|
52
57
|
|
53
|
-
# Checks if a option is
|
58
|
+
# Checks if a option is a Number
|
59
|
+
def validate_number(key, value)
|
60
|
+
if !value.kind_of?(Fixnum) && !value.kind_of?(NilClass)
|
61
|
+
I18n.t('vagrant_hostsprovisioner.error.not_a_number', {
|
62
|
+
:config_key => key,
|
63
|
+
:invalid_class => value.class.to_s
|
64
|
+
})
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Checks if a option is an Array or String
|
54
71
|
def validate_array_or_string(key, value)
|
55
|
-
if !
|
72
|
+
if !value.kind_of?(Array) && !value.kind_of?(String) && !value.kind_of?(NilClass)
|
56
73
|
I18n.t('vagrant_hostsprovisioner.error.not_an_array_or_string', {
|
57
74
|
:config_key => key,
|
58
75
|
:invalid_class => value.class.to_s
|
@@ -62,6 +79,30 @@ module VagrantPlugins
|
|
62
79
|
end
|
63
80
|
end
|
64
81
|
|
82
|
+
# Checks if a option is a String or Boolean
|
83
|
+
def validate_bool_or_string(key, value)
|
84
|
+
if ![TrueClass, FalseClass].include?(value.class) && !value.kind_of?(String) && !value.kind_of?(NilClass)
|
85
|
+
I18n.t('vagrant_hostsprovisioner.error.not_a_bool_or_string', {
|
86
|
+
:config_key => key,
|
87
|
+
:invalid_class => value.class.to_s
|
88
|
+
})
|
89
|
+
else
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Checks if a option is a String
|
95
|
+
def validate_string(key, value)
|
96
|
+
if !value.kind_of?(String) && !value.kind_of?(NilClass)
|
97
|
+
I18n.t('vagrant_hostsprovisioner.error.not_a_string', {
|
98
|
+
:config_key => key,
|
99
|
+
:invalid_class => value.class.to_s
|
100
|
+
})
|
101
|
+
else
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
65
106
|
end
|
66
107
|
end
|
67
108
|
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module HostsProvisioner
|
5
|
+
class Hosts
|
6
|
+
|
7
|
+
def initialize(machine, config)
|
8
|
+
@machine = machine
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def add
|
13
|
+
# Update the guest machine if manage_guest is enabled
|
14
|
+
if @config.manage_guest
|
15
|
+
update_guest
|
16
|
+
end
|
17
|
+
|
18
|
+
# Update the host machine if manage_host is enabled
|
19
|
+
if @config.manage_host
|
20
|
+
update_host(false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove
|
25
|
+
if @config.manage_host
|
26
|
+
update_host(true)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_guest
|
31
|
+
return unless @machine.communicate.ready?
|
32
|
+
|
33
|
+
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.update_guest"))
|
34
|
+
|
35
|
+
if (@machine.communicate.test("uname -s | grep SunOS"))
|
36
|
+
realhostfile = '/etc/inet/hosts'
|
37
|
+
move_cmd = 'mv'
|
38
|
+
elsif (@machine.communicate.test("test -d $Env:SystemRoot"))
|
39
|
+
windir = ""
|
40
|
+
@machine.communicate.execute("echo %SYSTEMROOT%", {:shell => :cmd}) do |type, contents|
|
41
|
+
windir << contents.gsub("\r\n", '') if type == :stdout
|
42
|
+
end
|
43
|
+
realhostfile = "#{windir}\\System32\\drivers\\etc\\hosts"
|
44
|
+
move_cmd = 'mv -force'
|
45
|
+
else
|
46
|
+
realhostfile = '/etc/hosts'
|
47
|
+
move_cmd = 'mv -f'
|
48
|
+
end
|
49
|
+
|
50
|
+
# download and modify file with Vagrant-managed entries
|
51
|
+
file = @machine.env.tmp_path.join("hosts.#{@machine.name}")
|
52
|
+
@machine.communicate.download(realhostfile, file)
|
53
|
+
if update_file(file, false, false)
|
54
|
+
# upload modified file and remove temporary file
|
55
|
+
@machine.communicate.upload(file, '/tmp/hosts')
|
56
|
+
@machine.communicate.sudo("#{move_cmd} /tmp/hosts #{realhostfile}")
|
57
|
+
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.hosts_file_updated", {:file => realhostfile}))
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
FileUtils.rm(file)
|
62
|
+
rescue Exception => e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_host(clean)
|
67
|
+
# copy and modify hosts file on host with Vagrant-managed entries
|
68
|
+
file = @machine.env.tmp_path.join('hosts.local')
|
69
|
+
|
70
|
+
if clean == true
|
71
|
+
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.clean_host"))
|
72
|
+
else
|
73
|
+
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.update_host"))
|
74
|
+
end
|
75
|
+
|
76
|
+
if WindowsSupport.windows?
|
77
|
+
# lazily include windows Module
|
78
|
+
class << self
|
79
|
+
include WindowsSupport unless include? WindowsSupport
|
80
|
+
end
|
81
|
+
|
82
|
+
hosts_location = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts"
|
83
|
+
copy_proc = Proc.new { windows_copy_file(file, hosts_location) }
|
84
|
+
else
|
85
|
+
hosts_location = '/etc/hosts'
|
86
|
+
copy_proc = Proc.new { `sudo cp #{file} #{hosts_location}` }
|
87
|
+
end
|
88
|
+
|
89
|
+
FileUtils.cp(hosts_location, file)
|
90
|
+
if update_file(file, true, clean)
|
91
|
+
copy_proc.call
|
92
|
+
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.hosts_file_updated", {:file => hosts_location}))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_file(file, include_id, clean)
|
97
|
+
file = Pathname.new(file)
|
98
|
+
old_file_content = file.read
|
99
|
+
new_file_content = update_content(old_file_content, include_id, clean)
|
100
|
+
file.open('w') { |io| io.write(new_file_content) }
|
101
|
+
old_file_content != new_file_content
|
102
|
+
end
|
103
|
+
|
104
|
+
def update_content(file_content, include_id, clean)
|
105
|
+
id = include_id ? " id: #{read_or_create_id}" : ""
|
106
|
+
header = "## vagrant-hosts-provisioner-start#{id}\n"
|
107
|
+
footer = "## vagrant-hosts-provisioner-end\n"
|
108
|
+
body = clean ? "" : get_hosts_file_entry
|
109
|
+
get_new_content(header, footer, body, file_content)
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_hosts_file_entry
|
113
|
+
# Get the vm ip address
|
114
|
+
ip = get_ip_address
|
115
|
+
|
116
|
+
# Return empy string if we don't have an ip address
|
117
|
+
if ip === nil
|
118
|
+
handle_comm(:stderr, I18n.t("vagrant_hostsprovisioner.error.no_vm_ip"))
|
119
|
+
return ''
|
120
|
+
end
|
121
|
+
|
122
|
+
hosts = []
|
123
|
+
|
124
|
+
# Add the machine hostname
|
125
|
+
unless @config.hostname === false
|
126
|
+
hosts.push(@config.hostname || @machine.config.vm.hostname || @machine.name)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add the defined aliases
|
130
|
+
hosts.concat(@config.aliases)
|
131
|
+
|
132
|
+
# Add the contents of the defined hosts files
|
133
|
+
if @config.files.count > 0
|
134
|
+
hosts.concat(get_files_data)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Remove duplicates
|
138
|
+
hosts = hosts.uniq
|
139
|
+
|
140
|
+
# Limit the number of hosts per line to 8
|
141
|
+
lines = []
|
142
|
+
hosts.each_slice(8) do |chnk|
|
143
|
+
lines.push("#{ip}\t" + chnk.join(' ').strip)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Join lines
|
147
|
+
hosts = lines.join("\n").strip
|
148
|
+
|
149
|
+
"#{hosts}\n"
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_files_data
|
153
|
+
require 'json'
|
154
|
+
data = []
|
155
|
+
@config.files.each do |file|
|
156
|
+
if file.kind_of?(String) && file != ""
|
157
|
+
file_path = File.join(@machine.env.root_path, file)
|
158
|
+
if File.exist?(file_path)
|
159
|
+
file_data = JSON.parse(File.read(file_path))
|
160
|
+
data.concat([ file_data ].flatten)
|
161
|
+
else
|
162
|
+
handle_comm(:stderr, I18n.t("vagrant_hostsprovisioner.error.file_not_found", {:file => file.to_s}))
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
data.collect(&:strip)
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_ip_address
|
170
|
+
ip = nil
|
171
|
+
@machine.config.vm.networks.each do |network|
|
172
|
+
key, options = network[0], network[1]
|
173
|
+
ip = options[:ip] if key == :private_network
|
174
|
+
break if ip
|
175
|
+
end
|
176
|
+
# If no ip is defined in private_network then use the ssh host ip instead
|
177
|
+
ip || (@machine.ssh_info ? @machine.ssh_info[:host] : nil)
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_new_content(header, footer, body, old_content)
|
181
|
+
if body.empty?
|
182
|
+
block = "\n"
|
183
|
+
else
|
184
|
+
block = "\n\n" + header + body + footer + "\n"
|
185
|
+
end
|
186
|
+
# Pattern for finding existing block
|
187
|
+
header_pattern = Regexp.quote(header)
|
188
|
+
footer_pattern = Regexp.quote(footer)
|
189
|
+
pattern = Regexp.new("\n*#{header_pattern}.*?#{footer_pattern}\n*", Regexp::MULTILINE)
|
190
|
+
# Replace existing block or append
|
191
|
+
old_content.match(pattern) ? old_content.sub(pattern, block) : old_content.rstrip + block
|
192
|
+
end
|
193
|
+
|
194
|
+
def read_or_create_id
|
195
|
+
file = Pathname.new("#{@machine.env.local_data_path}/hostsprovisioner/#{@machine.name}")
|
196
|
+
if (file.file?)
|
197
|
+
id = file.read.strip
|
198
|
+
else
|
199
|
+
id = SecureRandom.uuid
|
200
|
+
file.dirname.mkpath
|
201
|
+
file.open('w') { |f| f.write(id) }
|
202
|
+
end
|
203
|
+
id + "-" + @config.id.to_s
|
204
|
+
end
|
205
|
+
|
206
|
+
## Windows support for copying files, requesting elevated privileges if necessary
|
207
|
+
module WindowsSupport
|
208
|
+
require 'rbconfig'
|
209
|
+
|
210
|
+
def self.windows?
|
211
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
212
|
+
end
|
213
|
+
|
214
|
+
require 'win32ole' if windows?
|
215
|
+
|
216
|
+
def windows_copy_file(source, dest)
|
217
|
+
begin
|
218
|
+
# First, try Ruby copy
|
219
|
+
FileUtils.cp(source, dest)
|
220
|
+
rescue Errno::EACCES
|
221
|
+
# Access denied, try with elevated privileges
|
222
|
+
windows_copy_file_elevated(source, dest)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
def windows_copy_file_elevated(source, dest)
|
229
|
+
# copy command only supports backslashes as separators
|
230
|
+
source, dest = [source, dest].map { |s| s.to_s.gsub(/\//, '\\') }
|
231
|
+
|
232
|
+
# run 'cmd /C copy ...' with elevated privilege, minimized
|
233
|
+
copy_cmd = "copy \"#{source}\" \"#{dest}\""
|
234
|
+
WIN32OLE.new('Shell.Application').ShellExecute('cmd', "/C #{copy_cmd}", nil, 'runas', 7)
|
235
|
+
|
236
|
+
# Unfortunately, ShellExecute does not give us a status code,
|
237
|
+
# and it is non-blocking so we can't reliably compare the file contents
|
238
|
+
# to see if they were copied.
|
239
|
+
#
|
240
|
+
# If the user rejects the UAC prompt, vagrant will silently continue
|
241
|
+
# without updating the hostsfile.
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# This handles outputting the communication data back to the UI
|
246
|
+
def handle_comm(type, data)
|
247
|
+
if [:stderr, :stdout].include?(type)
|
248
|
+
# Output the data with the proper color based on the stream.
|
249
|
+
color = type == :stdout ? :green : :red
|
250
|
+
|
251
|
+
# Clear out the newline since we add one
|
252
|
+
data = data.chomp
|
253
|
+
return if data.empty?
|
254
|
+
|
255
|
+
options = {}
|
256
|
+
options[:color] = color
|
257
|
+
|
258
|
+
@machine.ui.info(data.chomp, options)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -15,14 +15,35 @@ module VagrantPlugins
|
|
15
15
|
DESC
|
16
16
|
|
17
17
|
config(:hostsupdate, :provisioner) do
|
18
|
-
|
18
|
+
require_relative 'config'
|
19
19
|
Config
|
20
20
|
end
|
21
21
|
|
22
22
|
provisioner(:hostsupdate) do
|
23
|
-
|
23
|
+
require_relative 'provisioner'
|
24
24
|
Provisioner
|
25
25
|
end
|
26
|
+
|
27
|
+
action_hook(:hostsupdate, :machine_action_resume) do |hook|
|
28
|
+
require_relative 'action'
|
29
|
+
hook.append(Action.add)
|
30
|
+
end
|
31
|
+
|
32
|
+
action_hook(:hostsupdate, :machine_action_suspend) do |hook|
|
33
|
+
require_relative 'action'
|
34
|
+
hook.prepend(Action.remove)
|
35
|
+
end
|
36
|
+
|
37
|
+
action_hook(:hostsupdate, :machine_action_halt) do |hook|
|
38
|
+
require_relative 'action'
|
39
|
+
hook.prepend(Action.remove)
|
40
|
+
end
|
41
|
+
|
42
|
+
action_hook(:hostsupdate, :machine_action_destroy) do |hook|
|
43
|
+
require_relative 'action'
|
44
|
+
hook.prepend(Action.remove)
|
45
|
+
end
|
46
|
+
|
26
47
|
end
|
27
48
|
end
|
28
49
|
end
|
@@ -1,239 +1,16 @@
|
|
1
|
-
require
|
1
|
+
require File.expand_path("../hosts", __FILE__)
|
2
2
|
|
3
3
|
module VagrantPlugins
|
4
4
|
module HostsProvisioner
|
5
5
|
class Provisioner < Vagrant.plugin('2', :provisioner)
|
6
6
|
|
7
7
|
def initialize(machine, config)
|
8
|
+
@hosts = Hosts.new(machine, config)
|
8
9
|
super
|
9
10
|
end
|
10
11
|
|
11
12
|
def provision
|
12
|
-
|
13
|
-
if @config.manage_guest
|
14
|
-
update_guest
|
15
|
-
end
|
16
|
-
|
17
|
-
# Update the host machine if manage_host is enabled
|
18
|
-
if @config.manage_host
|
19
|
-
update_host
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def update_guest
|
24
|
-
return unless @machine.communicate.ready?
|
25
|
-
|
26
|
-
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.update_guest"))
|
27
|
-
|
28
|
-
if (@machine.communicate.test("uname -s | grep SunOS"))
|
29
|
-
realhostfile = '/etc/inet/hosts'
|
30
|
-
move_cmd = 'mv'
|
31
|
-
elsif (@machine.communicate.test("test -d $Env:SystemRoot"))
|
32
|
-
windir = ""
|
33
|
-
@machine.communicate.execute("echo %SYSTEMROOT%", {:shell => :cmd}) do |type, contents|
|
34
|
-
windir << contents.gsub("\r\n", '') if type == :stdout
|
35
|
-
end
|
36
|
-
realhostfile = "#{windir}\\System32\\drivers\\etc\\hosts"
|
37
|
-
move_cmd = 'mv -force'
|
38
|
-
else
|
39
|
-
realhostfile = '/etc/hosts'
|
40
|
-
move_cmd = 'mv -f'
|
41
|
-
end
|
42
|
-
|
43
|
-
# download and modify file with Vagrant-managed entries
|
44
|
-
file = @machine.env.tmp_path.join("hosts.#{@machine.name}")
|
45
|
-
@machine.communicate.download(realhostfile, file)
|
46
|
-
if update_file(file, false)
|
47
|
-
# upload modified file and remove temporary file
|
48
|
-
@machine.communicate.upload(file, '/tmp/hosts')
|
49
|
-
@machine.communicate.sudo("#{move_cmd} /tmp/hosts #{realhostfile}")
|
50
|
-
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.hosts_file_updated", {:file => realhostfile}))
|
51
|
-
end
|
52
|
-
|
53
|
-
begin
|
54
|
-
FileUtils.rm(file)
|
55
|
-
rescue Exception => e
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def update_host
|
60
|
-
# copy and modify hosts file on host with Vagrant-managed entries
|
61
|
-
file = @machine.env.tmp_path.join('hosts.local')
|
62
|
-
|
63
|
-
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.update_host"))
|
64
|
-
|
65
|
-
if WindowsSupport.windows?
|
66
|
-
# lazily include windows Module
|
67
|
-
class << self
|
68
|
-
include WindowsSupport unless include? WindowsSupport
|
69
|
-
end
|
70
|
-
|
71
|
-
hosts_location = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts"
|
72
|
-
copy_proc = Proc.new { windows_copy_file(file, hosts_location) }
|
73
|
-
else
|
74
|
-
hosts_location = '/etc/hosts'
|
75
|
-
copy_proc = Proc.new { `sudo cp #{file} #{hosts_location}` }
|
76
|
-
end
|
77
|
-
|
78
|
-
FileUtils.cp(hosts_location, file)
|
79
|
-
if update_file(file, true)
|
80
|
-
copy_proc.call
|
81
|
-
handle_comm(:stdout, I18n.t("vagrant_hostsprovisioner.provisioner.hosts_file_updated", {:file => hosts_location}))
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def update_file(file, include_id)
|
86
|
-
file = Pathname.new(file)
|
87
|
-
old_file_content = file.read
|
88
|
-
new_file_content = update_content(old_file_content, include_id)
|
89
|
-
file.open('w') { |io| io.write(new_file_content) }
|
90
|
-
old_file_content != new_file_content
|
91
|
-
end
|
92
|
-
|
93
|
-
def update_content(file_content, include_id)
|
94
|
-
id = include_id ? " id: #{read_or_create_id}" : ""
|
95
|
-
header = "## vagrant-hosts-provisioner-start#{id}\n"
|
96
|
-
footer = "## vagrant-hosts-provisioner-end\n"
|
97
|
-
body = get_hosts_file_entry
|
98
|
-
get_new_content(header, footer, body, file_content)
|
99
|
-
end
|
100
|
-
|
101
|
-
def get_hosts_file_entry
|
102
|
-
# Get the vm ip address
|
103
|
-
ip = get_ip_address
|
104
|
-
|
105
|
-
# Return empy string if we don't have an ip address
|
106
|
-
if ip === nil
|
107
|
-
handle_comm(:stderr, I18n.t("vagrant_hostsprovisioner.error.no_vm_ip"))
|
108
|
-
return ''
|
109
|
-
end
|
110
|
-
|
111
|
-
# Add the machine hostname
|
112
|
-
hosts = []
|
113
|
-
unless @config.hostname === false
|
114
|
-
hosts.push(@config.hostname || @machine.config.vm.hostname || @machine.name)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Add the defined aliases
|
118
|
-
hosts.concat(@config.aliases)
|
119
|
-
|
120
|
-
# Add the contents of the defined hosts files
|
121
|
-
if @config.files.count > 0
|
122
|
-
hosts.concat(get_files_data)
|
123
|
-
end
|
124
|
-
|
125
|
-
# Remove duplicates
|
126
|
-
hosts = hosts.uniq.join(' ').strip
|
127
|
-
|
128
|
-
"#{ip}\t#{hosts}\n"
|
129
|
-
end
|
130
|
-
|
131
|
-
def get_files_data
|
132
|
-
require 'json'
|
133
|
-
data = []
|
134
|
-
@config.files.each do |file|
|
135
|
-
file_path = File.join(@machine.env.root_path, file)
|
136
|
-
if File.exist?(file_path)
|
137
|
-
file_data = JSON.parse(File.read(file_path))
|
138
|
-
data.concat([ file_data ].flatten)
|
139
|
-
else
|
140
|
-
handle_comm(:stderr, I18n.t("vagrant_hostsprovisioner.error.file_not_found", {:file => file.to_s}))
|
141
|
-
end
|
142
|
-
end
|
143
|
-
data.collect(&:strip)
|
144
|
-
end
|
145
|
-
|
146
|
-
def get_ip_address
|
147
|
-
ip = nil
|
148
|
-
@machine.config.vm.networks.each do |network|
|
149
|
-
key, options = network[0], network[1]
|
150
|
-
ip = options[:ip] if key == :private_network
|
151
|
-
break if ip
|
152
|
-
end
|
153
|
-
# If no ip is defined in private_network then use the ssh host ip instead
|
154
|
-
ip || (@machine.ssh_info ? @machine.ssh_info[:host] : nil)
|
155
|
-
end
|
156
|
-
|
157
|
-
def get_new_content(header, footer, body, old_content)
|
158
|
-
if body.empty?
|
159
|
-
block = "\n"
|
160
|
-
else
|
161
|
-
block = "\n\n" + header + body + footer + "\n"
|
162
|
-
end
|
163
|
-
# Pattern for finding existing block
|
164
|
-
header_pattern = Regexp.quote(header)
|
165
|
-
footer_pattern = Regexp.quote(footer)
|
166
|
-
pattern = Regexp.new("\n*#{header_pattern}.*?#{footer_pattern}\n*", Regexp::MULTILINE)
|
167
|
-
# Replace existing block or append
|
168
|
-
old_content.match(pattern) ? old_content.sub(pattern, block) : old_content.rstrip + block
|
169
|
-
end
|
170
|
-
|
171
|
-
def read_or_create_id
|
172
|
-
file = Pathname.new("#{@machine.env.local_data_path}/hostsprovisioner/#{@machine.name}")
|
173
|
-
if (file.file?)
|
174
|
-
id = file.read.strip
|
175
|
-
else
|
176
|
-
id = SecureRandom.uuid
|
177
|
-
file.dirname.mkpath
|
178
|
-
file.open('w') { |f| f.write(id) }
|
179
|
-
end
|
180
|
-
id
|
181
|
-
end
|
182
|
-
|
183
|
-
## Windows support for copying files, requesting elevated privileges if necessary
|
184
|
-
module WindowsSupport
|
185
|
-
require 'rbconfig'
|
186
|
-
|
187
|
-
def self.windows?
|
188
|
-
RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
189
|
-
end
|
190
|
-
|
191
|
-
require 'win32ole' if windows?
|
192
|
-
|
193
|
-
def windows_copy_file(source, dest)
|
194
|
-
begin
|
195
|
-
# First, try Ruby copy
|
196
|
-
FileUtils.cp(source, dest)
|
197
|
-
rescue Errno::EACCES
|
198
|
-
# Access denied, try with elevated privileges
|
199
|
-
windows_copy_file_elevated(source, dest)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
private
|
204
|
-
|
205
|
-
def windows_copy_file_elevated(source, dest)
|
206
|
-
# copy command only supports backslashes as separators
|
207
|
-
source, dest = [source, dest].map { |s| s.to_s.gsub(/\//, '\\') }
|
208
|
-
|
209
|
-
# run 'cmd /C copy ...' with elevated privilege, minimized
|
210
|
-
copy_cmd = "copy \"#{source}\" \"#{dest}\""
|
211
|
-
WIN32OLE.new('Shell.Application').ShellExecute('cmd', "/C #{copy_cmd}", nil, 'runas', 7)
|
212
|
-
|
213
|
-
# Unfortunately, ShellExecute does not give us a status code,
|
214
|
-
# and it is non-blocking so we can't reliably compare the file contents
|
215
|
-
# to see if they were copied.
|
216
|
-
#
|
217
|
-
# If the user rejects the UAC prompt, vagrant will silently continue
|
218
|
-
# without updating the hostsfile.
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# This handles outputting the communication data back to the UI
|
223
|
-
def handle_comm(type, data)
|
224
|
-
if [:stderr, :stdout].include?(type)
|
225
|
-
# Output the data with the proper color based on the stream.
|
226
|
-
color = type == :stdout ? :green : :red
|
227
|
-
|
228
|
-
# Clear out the newline since we add one
|
229
|
-
data = data.chomp
|
230
|
-
return if data.empty?
|
231
|
-
|
232
|
-
options = {}
|
233
|
-
options[:color] = color
|
234
|
-
|
235
|
-
@machine.ui.info(data.chomp, options)
|
236
|
-
end
|
13
|
+
@hosts.add
|
237
14
|
end
|
238
15
|
|
239
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vagrant-hosts-provisioner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: '2.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mohamed Elkholy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,7 +52,11 @@ files:
|
|
52
52
|
- README.md
|
53
53
|
- Rakefile
|
54
54
|
- lib/vagrant-hosts-provisioner.rb
|
55
|
+
- lib/vagrant-hosts-provisioner/action.rb
|
56
|
+
- lib/vagrant-hosts-provisioner/action/add.rb
|
57
|
+
- lib/vagrant-hosts-provisioner/action/remove.rb
|
55
58
|
- lib/vagrant-hosts-provisioner/config.rb
|
59
|
+
- lib/vagrant-hosts-provisioner/hosts.rb
|
56
60
|
- lib/vagrant-hosts-provisioner/plugin.rb
|
57
61
|
- lib/vagrant-hosts-provisioner/provisioner.rb
|
58
62
|
- lib/vagrant-hosts-provisioner/version.rb
|
@@ -84,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
88
|
version: '0'
|
85
89
|
requirements: []
|
86
90
|
rubyforge_project: vagrant-hosts-provisioner
|
87
|
-
rubygems_version: 2.4.
|
91
|
+
rubygems_version: 2.4.5
|
88
92
|
signing_key:
|
89
93
|
specification_version: 4
|
90
94
|
summary: A Vagrant provisioner for managing the /etc/hosts file of the host and guest
|