Explicitly define LF or CRLF line endings for hosts and guests when defining the hosts file content.

This commit is contained in:
Seth Reeser 2017-05-03 15:25:59 -04:00
parent e97bc6fd16
commit 42bfe1f82b
4 changed files with 48 additions and 35 deletions

View File

@ -1,7 +1,7 @@
source 'https://rubygems.org' source 'https://rubygems.org'
group :development do group :development do
gem 'vagrant', :git => 'git://github.com/mitchellh/vagrant.git', :tag => 'v1.6.2' gem 'vagrant', :git => 'git://github.com/mitchellh/vagrant.git', :tag => 'v1.9.4'
end end
group :plugins do group :plugins do

View File

@ -6,7 +6,7 @@ Vagrant Host Manager
[![Gem](https://img.shields.io/gem/dtv/vagrant-hostmanager.svg)](https://rubygems.org/gems/vagrant-hostmanager) [![Gem](https://img.shields.io/gem/dtv/vagrant-hostmanager.svg)](https://rubygems.org/gems/vagrant-hostmanager)
[![Twitter](https://img.shields.io/twitter/url/https/github.com/devopsgroup-io/vagrant-hostmanager.svg?style=social)](https://twitter.com/intent/tweet?text=Check%20out%20this%20awesome%20Vagrant%20plugin%21&url=https%3A%2F%2Fgithub.com%devopsgroup-io%2Fvagrant-hostmanager&hashtags=vagrant%hostmanager&original_referer=) [![Twitter](https://img.shields.io/twitter/url/https/github.com/devopsgroup-io/vagrant-hostmanager.svg?style=social)](https://twitter.com/intent/tweet?text=Check%20out%20this%20awesome%20Vagrant%20plugin%21&url=https%3A%2F%2Fgithub.com%devopsgroup-io%2Fvagrant-hostmanager&hashtags=vagrant%hostmanager&original_referer=)
`vagrant-hostmanager` is a plugin that manages the `hosts` 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. `vagrant-hostmanager` is a plugin that manages the `/etc/hosts` 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 Installation
------------ ------------
@ -16,7 +16,7 @@ Install the plugin following the typical Vagrant 1.1 procedure:
Usage Usage
----- -----
To update the `hosts` file on each active machine, run the following To update the `/etc/hosts` file on each active machine, run the following
command: command:
$ vagrant hostmanager $ vagrant hostmanager
@ -24,14 +24,14 @@ command:
The plugin hooks into the `vagrant up` and `vagrant destroy` commands The plugin hooks into the `vagrant up` and `vagrant destroy` commands
automatically. automatically.
When a machine enters or exits the running state , all active When a machine enters or exits the running state , all active
machines with the same provider will have their `hosts` file updated machines with the same provider will have their `/etc/hosts` file updated
accordingly. Set the `hostmanager.enabled` attribute to `true` in the accordingly. Set the `hostmanager.enabled` attribute to `true` in the
Vagrantfile to activate this behavior. Vagrantfile to activate this behavior.
To update the host's `hosts` file, set the `hostmanager.manage_host` To update the host's `/etc/hosts` file, set the `hostmanager.manage_host`
attribute to `true`. attribute to `true`.
To update the guests' `hosts` file, set the `hostmanager.manage_guest` To update the guests' `/etc/hosts` file, set the `hostmanager.manage_guest`
attribute to `true`. attribute to `true`.
A machine's IP address is defined by either the static IP for a private A machine's IP address is defined by either the static IP for a private
@ -90,14 +90,14 @@ config.vm.provision :hostmanager
Custom IP resolver Custom IP resolver
------------------ ------------------
You can customize how vagrant-hostmanager resolves IP address You can customize way, how host manager resolves IP address
for each machine. This might be handy in the case of the AWS provider, for each machine. This might be handy in case of aws provider,
where the host name is stored in the ssh_info hash of each machine. where host name is stored in ssh_info hash of each machine.
This causes a generation of an invalid `hosts` file. This causes generation of invalid /etc/hosts file.
A custom IP resolver gives you the oportunity to calculate IP address Custom IP resolver gives you oportunity to calculate IP address
for each machine by yourself, giving you access to the machine that is for each machine by yourself, giving You also access to the machine that is
updating `hosts`. For example: updating /etc/hosts. For example:
```ruby ```ruby
config.hostmanager.ip_resolver = proc do |vm, resolving_vm| config.hostmanager.ip_resolver = proc do |vm, resolving_vm|
@ -188,11 +188,12 @@ To contribute, fork then clone the repository, and then the following:
**Developing** **Developing**
1. Install [Bundler](http://bundler.io/) 1. Ideally, install the version of Vagrant as defined in the `Gemfile`
2. Currently the Bundler version is locked to 1.6.9, please install this version. 1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/)
* `sudo gem install bundler -v '1.6.9'` 2. Currently the Bundler version is locked to 1.14.6, please install this version.
* `gem install bundler -v '1.14.6'`
3. Then install vagrant-hostmanager dependancies: 3. Then install vagrant-hostmanager dependancies:
* `bundle _1.6.9_ install` * `bundle _1.14.6_ install`
**Testing** **Testing**

View File

@ -18,16 +18,20 @@ module VagrantPlugins
return unless machine.communicate.ready? return unless machine.communicate.ready?
if (machine.communicate.test("uname -s | grep SunOS")) if (machine.communicate.test("uname -s | grep SunOS"))
realhostfile = '/etc/inet/hosts' realhostfile = "/etc/inet/hosts"
line_endings = "lf"
elsif (machine.communicate.test("test -d $Env:SystemRoot")) elsif (machine.communicate.test("test -d $Env:SystemRoot"))
windir = "" windir = ""
machine.communicate.execute("echo %SYSTEMROOT%", {:shell => :cmd}) do |type, contents| machine.communicate.execute("echo %SYSTEMROOT%", {:shell => :cmd}) do |type, contents|
windir << contents.gsub("\r\n", '') if type == :stdout windir << contents.gsub("\r\n", '') if type == :stdout
end end
realhostfile = "#{windir}\\System32\\drivers\\etc\\hosts" realhostfile = "#{windir}\\System32\\drivers\\etc\\hosts"
line_endings = "crlf"
else else
realhostfile = '/etc/hosts' realhostfile = "/etc/hosts"
line_endings = "lf"
end end
# download and modify file with Vagrant-managed entries # download and modify file with Vagrant-managed entries
file = @global_env.tmp_path.join("hosts.#{machine.name}") file = @global_env.tmp_path.join("hosts.#{machine.name}")
machine.communicate.download(realhostfile, file) machine.communicate.download(realhostfile, file)
@ -35,8 +39,7 @@ module VagrantPlugins
@logger.debug("file is: #{file.to_s}") @logger.debug("file is: #{file.to_s}")
@logger.debug("class of file is: #{file.class}") @logger.debug("class of file is: #{file.class}")
if update_file(file, machine, false) if update_file(file, machine, false, line_endings)
# upload modified file and remove temporary file # upload modified file and remove temporary file
machine.communicate.upload(file.to_s, '/tmp/hosts') machine.communicate.upload(file.to_s, '/tmp/hosts')
if windir if windir
@ -57,38 +60,40 @@ module VagrantPlugins
class << self class << self
include WindowsSupport unless include? WindowsSupport include WindowsSupport unless include? WindowsSupport
end end
hosts_location = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts" hosts_location = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts"
copy_proc = Proc.new { windows_copy_file(file, hosts_location) } copy_proc = Proc.new { windows_copy_file(file, hosts_location) }
line_endings = "crlf"
else else
hosts_location = '/etc/hosts' hosts_location = '/etc/hosts'
copy_proc = Proc.new { `[ -w #{hosts_location} ] && cat #{file} > #{hosts_location} || sudo cp #{file} #{hosts_location}` } copy_proc = Proc.new { `[ -w #{hosts_location} ] && cat #{file} > #{hosts_location} || sudo cp #{file} #{hosts_location}` }
line_endings = "lf"
end end
FileUtils.cp(hosts_location, file) FileUtils.cp(hosts_location, file)
if update_file(file)
if update_file(file, nil, true, line_endings)
copy_proc.call copy_proc.call
end end
end end
private private
def update_file(file, resolving_machine = nil, include_id = true) def update_file(file, resolving_machine = nil, include_id = true, line_endings)
file = Pathname.new(file) file = Pathname.new(file)
old_file_content = file.read old_file_content = file.read
new_file_content = update_content(old_file_content, resolving_machine, include_id) new_file_content = update_content(old_file_content, resolving_machine, include_id, line_endings)
file.open('wb') { |io| io.write(new_file_content) } file.open('wb') { |io| io.write(new_file_content) }
old_file_content != new_file_content old_file_content != new_file_content
end end
def update_content(file_content, resolving_machine, include_id) def update_content(file_content, resolving_machine, include_id, line_endings)
id = include_id ? " id: #{read_or_create_id}" : "" id = include_id ? " id: #{read_or_create_id}" : ""
header = "## vagrant-hostmanager-start#{id}\n" header = "## vagrant-hostmanager-start#{id}\n"
footer = "## vagrant-hostmanager-end\n" footer = "## vagrant-hostmanager-end\n"
body = get_machines body = get_machines
.map { |machine| get_hosts_file_entry(machine, resolving_machine) } .map { |machine| get_hosts_file_entry(machine, resolving_machine) }
.join .join
get_new_content(header, footer, body, file_content) get_new_content(header, footer, body, file_content, line_endings)
end end
def get_hosts_file_entry(machine, resolving_machine) def get_hosts_file_entry(machine, resolving_machine)
@ -137,7 +142,7 @@ module VagrantPlugins
.reject(&:nil?) .reject(&:nil?)
end end
def get_new_content(header, footer, body, old_content) def get_new_content(header, footer, body, old_content, line_endings)
if body.empty? if body.empty?
block = "\n" block = "\n"
else else
@ -148,7 +153,14 @@ module VagrantPlugins
footer_pattern = Regexp.quote(footer) footer_pattern = Regexp.quote(footer)
pattern = Regexp.new("\n*#{header_pattern}.*?#{footer_pattern}\n*", Regexp::MULTILINE) pattern = Regexp.new("\n*#{header_pattern}.*?#{footer_pattern}\n*", Regexp::MULTILINE)
# Replace existing block or append # Replace existing block or append
old_content.match(pattern) ? old_content.sub(pattern, block) : old_content.rstrip + block content = old_content.match(pattern) ? old_content.sub(pattern, block) : old_content.rstrip + block
if line_endings == "crlf"
content.encode(content.encoding, :universal_encoding => true).encode(content.encoding, :crlf_newline => true)
elsif line_endings == "lf"
content.encode(content.encoding, :universal_encoding => true)
else
content.encode(content.encoding, :universal_encoding => true)
end
end end
def read_or_create_id def read_or_create_id

View File

@ -1,10 +1,10 @@
en: en:
vagrant_hostmanager: vagrant_hostmanager:
action: action:
update_guests: "Updating /etc/hosts file on active guest machines..." update_guests: "[vagrant-hostmanager:guests] Updating hosts file on active guest virtual machines..."
update_guest: "[%{name}] Updating /etc/hosts file..." update_guest: "[vagrant-hostmanager:guest] Updating hosts file on the virtual machine %{name}..."
update_host: "Updating /etc/hosts file on host machine (password may be required)..." update_host: "[vagrant-hostmanager:host] Updating hosts file on your workstation (password may be required)..."
config: config:
not_a_bool: "A value for %{config_key} can only be true or false, not type '%{value}'" not_a_bool: "[vagrant-hostmanager:config:error] A value for %{config_key} can only be true or false, not type '%{value}'"
not_an_array_or_string: "A value for %{config_key} must be an Array or String, not type '%{is_class}'" not_an_array_or_string: "[vagrant-hostmanager:config:error] A value for %{config_key} must be an Array or String, not type '%{is_class}'"
not_a_proc: "A value for %{config_key} must be a Proc, not type '%{is_class}'" not_a_proc: "[vagrant-hostmanager:config:error] A value for %{config_key} must be a Proc, not type '%{is_class}'"