├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── vagrant-ghost.rb └── vagrant-ghost │ ├── Action │ ├── CacheHosts.rb │ ├── RemoveHosts.rb │ └── UpdateHosts.rb │ ├── Ghost.rb │ ├── command.rb │ ├── config.rb │ ├── plugin.rb │ └── version.rb └── vagrant-ghost.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *gem 2 | .bindle 3 | .config 4 | Gemfile.lock 5 | doc 6 | pkg 7 | vendor 8 | .idea -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in vagrant-ghost.gemspec 4 | gemspec 5 | 6 | group :development do 7 | gem "vagrant", git: "https://github.com/mitchellh/vagrant.git" 8 | end 9 | 10 | group :plugins do 11 | gem "vagrant-ghost", path: "." 12 | end 13 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 10up, John P Bloch, Eric Mann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vagrant::Ghost 2 | 3 | [![Gem Version](https://badge.fury.io/rb/vagrant-ghost.svg)](http://badge.fury.io/rb/vagrant-ghost) 4 | 5 | This plugin adds an entry to your /etc/hosts file on the host system. 6 | 7 | On **up**, **resume** and **reload** commands, it tries to add the information, if its not already in your hosts file. If it needs to be added, you will be asked for an administrator password, since it uses sudo to edit the file. 8 | 9 | On **halt**, **destroy**, and **suspend** those entries will be removed again. 10 | 11 | ## Installation 12 | 13 | $ vagrant plugin install vagrant-ghost 14 | 15 | Uninstall it with: 16 | 17 | $ vagrant plugin uninstall vagrant-ghost 18 | 19 | ## Usage 20 | 21 | At the moment, the only things you need, are the hostname and a :private_network network with a fixed ip. 22 | 23 | config.vm.network :private_network, ip: "192.168.3.10" 24 | config.vm.hostname = "www.testing.de" 25 | config.ghost.hosts = ["alias.testing.de", "alias2.somedomain.com"] 26 | 27 | This ip and the hostname will be used for the entry in the /etc/hosts file. 28 | 29 | Additional aliases can be added by creating an `/aliases` file at the root of the Vagrant machine installation with one host alias per line. This file will be re-imported whenever Vagrant Ghost updates the hostsfile. 30 | 31 | You may change the name of the aliases file by setting the `hosts_files` configuration option in your Vagrantfile: 32 | 33 | config.ghost.hosts_files = "hosts_aliases" # Could be anything, e.g. "hosts", or whatever 34 | 35 | This will scan the vagrant directory for any file with the name you configured and will use each line as a URI to add to your hosts file pointing to that vagrant instance. 36 | 37 | ## Changelog 38 | 39 | ### 0.2.3 40 | * Fixed allowed_push_host setting to allow pushing to rubygems.org 41 | 42 | ### 0.2.2 43 | * Only parse files for host names, not directories 44 | * Add a configuration option `hosts_files` to change the name of the file that holds aliases 45 | 46 | ### 0.2.1 47 | * Make the search for `aliases` target the VM's root directory 48 | 49 | ### 0.2.0 50 | * Use `ghost.config.hosts` to set static hosts while pulling dynamic ones from an `/aliases` file 51 | * Update documentation 52 | 53 | ### 0.1.3 54 | * Consolidate `/config/hosts` and `/aliases` to just `/**/aliases` 55 | 56 | ### 0.1.2 57 | * Make the CLI command scan a local hosts setup files (`/config/hosts` and `/aliases`) to rebuild the hosts map 58 | 59 | ### 0.1.1 60 | * Update the CLI to a "primary" command 61 | * Make sure help doesn't try to fire a host update 62 | 63 | ### 0.1.0 64 | * Initial release 65 | 66 | ## Contributing 67 | 68 | 1. Fork it 69 | 2. Create your feature branch (`git checkout -b my-new-feature`) 70 | 3. Commit your changes (`git commit -am 'Add some feature'`) 71 | 4. Push to the branch (`git push origin my-new-feature`) 72 | 5. Create new Pull Request 73 | 74 | ## Credits 75 | 76 | This is a fork of [vagrant-hostsupdater](https://github.com/cogitatio/vagrant-hostsupdater). 77 | ``` 78 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /lib/vagrant-ghost.rb: -------------------------------------------------------------------------------- 1 | require "vagrant-ghost/version" 2 | require "vagrant-ghost/plugin" 3 | 4 | module VagrantPlugins 5 | module Ghost 6 | def self.source_root 7 | @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/vagrant-ghost/Action/CacheHosts.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Ghost 3 | module Action 4 | class CacheHosts 5 | include Ghost 6 | 7 | def initialize(app, env) 8 | @app = app 9 | @machine = env[:machine] 10 | end 11 | 12 | def call(env) 13 | cacheHostEntries 14 | @app.call(env) 15 | end 16 | 17 | end 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /lib/vagrant-ghost/Action/RemoveHosts.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Ghost 3 | module Action 4 | class RemoveHosts 5 | include Ghost 6 | 7 | def initialize(app, env) 8 | @app = app 9 | @machine = env[:machine] 10 | @ui = env[:ui] 11 | end 12 | 13 | def call(env) 14 | @ui.info "Removing hosts" 15 | removeHostEntries 16 | @app.call(env) 17 | end 18 | 19 | end 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/vagrant-ghost/Action/UpdateHosts.rb: -------------------------------------------------------------------------------- 1 | require_relative "../Ghost" 2 | module VagrantPlugins 3 | module Ghost 4 | module Action 5 | class UpdateHosts 6 | include Ghost 7 | 8 | 9 | def initialize(app, env) 10 | @app = app 11 | @machine = env[:machine] 12 | @ui = env[:ui] 13 | end 14 | 15 | def call(env) 16 | @ui.info "Checking for host entries" 17 | @app.call(env) 18 | addHostEntries() 19 | end 20 | 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/vagrant-ghost/Ghost.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Ghost 3 | module Ghost 4 | @@hosts_path = Vagrant::Util::Platform.windows? ? File.expand_path('system32/drivers/etc/hosts', ENV['windir']) : '/etc/hosts' 5 | 6 | def getIps 7 | ips = [] 8 | @machine.config.vm.networks.each do |network| 9 | key, options = network[0], network[1] 10 | ip = options[:ip] if key == :private_network 11 | ips.push(ip) if ip 12 | end 13 | 14 | begin 15 | network = @machine.provider.driver.read_guest_ip 16 | if network['ip'] 17 | ips.push( network['ip'] ) unless ips.include? network['ip'] 18 | end 19 | rescue 20 | end 21 | 22 | return ips 23 | end 24 | 25 | def getHostnames 26 | hostnames = Array(@machine.config.vm.hostname) 27 | 28 | hosts_files = @machine.config.ghost.hosts_files || 'aliases' 29 | 30 | # Regenerate hosts from aliases file 31 | paths = Dir[File.join( @machine.env.root_path.to_s, '**', hosts_files )] 32 | aliases = paths.map do |path| 33 | if File.file?(path) 34 | lines = File.readlines(path).map(&:chomp) 35 | lines.grep(/\A[^#]/) 36 | end 37 | end.flatten.uniq 38 | 39 | # Concat with the local hostname 40 | hostnames.concat( aliases ) 41 | 42 | # Fetch static hosts from the configuration 43 | if @machine.config.ghost.hosts 44 | hostnames.concat( @machine.config.ghost.hosts ) 45 | end 46 | 47 | return hostnames 48 | end 49 | 50 | def addHostEntries 51 | ips = getIps 52 | hostnames = getHostnames 53 | file = File.open(@@hosts_path, "rb") 54 | hostsContents = file.read 55 | uuid = @machine.id 56 | name = @machine.name 57 | entries = [] 58 | ips.each do |ip| 59 | hostEntries = getHostEntries(ip, hostnames, name, uuid) 60 | hostEntries.each do |hostEntry| 61 | escapedEntry = Regexp.quote(hostEntry) 62 | if !hostsContents.match(/#{escapedEntry}/) 63 | @ui.info "adding to (#@@hosts_path) : #{hostEntry}" 64 | entries.push(hostEntry) 65 | end 66 | end 67 | end 68 | addToHosts(entries) 69 | end 70 | 71 | def cacheHostEntries 72 | @machine.config.ghost.id = @machine.id 73 | end 74 | 75 | def removeHostEntries 76 | if !@machine.id and !@machine.config.ghost.id 77 | @ui.warn "No machine id, nothing removed from #@@hosts_path" 78 | return 79 | end 80 | file = File.open(@@hosts_path, "rb") 81 | hostsContents = file.read 82 | uuid = @machine.id || @machine.config.ghost.id 83 | hashedId = Digest::MD5.hexdigest(uuid) 84 | if hostsContents.match(/#{hashedId}/) 85 | removeFromHosts 86 | end 87 | end 88 | 89 | def host_entry(ip, hostnames, name, uuid = self.uuid) 90 | %Q(#{ip} #{hostnames.join(' ')} #{signature(name, uuid)}) 91 | end 92 | 93 | def getHostEntries(ip, hostnames, name, uuid = self.uuid) 94 | entries = [] 95 | hostnames.each do |hostname| 96 | entries.push(%Q(#{ip} #{hostname} #{signature(name, uuid)})) 97 | end 98 | return entries 99 | end 100 | 101 | def addToHosts(entries) 102 | return if entries.length == 0 103 | content = entries.join("\n").strip 104 | if !File.writable?(@@hosts_path) 105 | sudo(%Q(sh -c 'echo "#{content}" >> #@@hosts_path')) 106 | else 107 | content = "\n" + content 108 | hostsFile = File.open(@@hosts_path, "a") 109 | hostsFile.write(content) 110 | hostsFile.close() 111 | end 112 | end 113 | 114 | def removeFromHosts(options = {}) 115 | uuid = @machine.id || @machine.config.ghost.id 116 | hashedId = Digest::MD5.hexdigest(uuid) 117 | if !File.writable?(@@hosts_path) 118 | sudo(%Q(sed -i -e '/#{hashedId}/ d' #@@hosts_path)) 119 | else 120 | hosts = "" 121 | File.open(@@hosts_path).each do |line| 122 | hosts << line unless line.include?(hashedId) 123 | end 124 | hostsFile = File.open(@@hosts_path, "w") 125 | hostsFile.write(hosts) 126 | hostsFile.close() 127 | end 128 | end 129 | 130 | 131 | 132 | def signature(name, uuid = self.uuid) 133 | hashedId = Digest::MD5.hexdigest(uuid) 134 | %Q(# VAGRANT: #{hashedId} (#{name}) / #{uuid}) 135 | end 136 | 137 | def sudo(command) 138 | return if !command 139 | if Vagrant::Util::Platform.windows? 140 | `#{command}` 141 | else 142 | `sudo #{command}` 143 | end 144 | end 145 | end 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /lib/vagrant-ghost/command.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Ghost 3 | class Command < Vagrant.plugin('2', :command) 4 | include Ghost 5 | 6 | def self.synopsis 7 | 'manages hosts files internally and externally' 8 | end 9 | 10 | def execute 11 | options = {} 12 | options[:provider] = @env.default_provider 13 | 14 | opts = OptionParser.new do |o| 15 | o.banner = 'Usage: vagrant ghost' 16 | o.separator '' 17 | 18 | o.on('-h', '--help', 'Print this help') do 19 | safe_puts(opts.help) 20 | return 21 | end 22 | 23 | o.on('--provider provider', String, 'Update machines with the specific provider.') do |provider| 24 | options[:provider] = provider 25 | end 26 | end 27 | 28 | argv = parse_options(opts) 29 | return if !argv 30 | 31 | @ui = @env.ui 32 | 33 | with_target_vms(argv, reverse: true) do |machine| 34 | @machine = machine 35 | 36 | # Always remove hosts to make sure old entries don't stick around 37 | removeHostEntries 38 | # Add all hosts again if the vm is up 39 | addHostEntries if is_running? 40 | end 41 | 0 42 | rescue Exception => e 43 | @env.ui.error "Something went wrong!" 44 | @env.ui.error " #{e.message}" 45 | @env.ui.error " #{e.backtrace.inspect}" 46 | 1 47 | end 48 | 49 | protected 50 | 51 | def is_running?(machine=nil) 52 | machine ||= @machine 53 | 54 | case machine.provider_name.to_sym 55 | when :hyperv, :virtualbox 56 | return :running == machine.state.id 57 | end 58 | 59 | false 60 | end 61 | 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/vagrant-ghost/config.rb: -------------------------------------------------------------------------------- 1 | require "vagrant" 2 | 3 | module VagrantPlugins 4 | module Ghost 5 | class Config < Vagrant.plugin("2", :config) 6 | attr_accessor :hosts 7 | attr_accessor :hosts_files 8 | attr_accessor :id 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/vagrant-ghost/plugin.rb: -------------------------------------------------------------------------------- 1 | require "vagrant-ghost/Action/UpdateHosts" 2 | require "vagrant-ghost/Action/CacheHosts" 3 | require "vagrant-ghost/Action/RemoveHosts" 4 | 5 | module VagrantPlugins 6 | module Ghost 7 | class Plugin < Vagrant.plugin('2') 8 | name 'Ghost' 9 | description <<-DESC 10 | This plugin manages your hosts files internally and externally 11 | DESC 12 | 13 | config(:ghost) do 14 | require_relative 'config' 15 | Config 16 | end 17 | 18 | action_hook(:ghost, :machine_action_up) do |hook| 19 | hook.append(Action::UpdateHosts) 20 | end 21 | 22 | action_hook(:ghost, :machine_action_halt) do |hook| 23 | hook.append(Action::RemoveHosts) 24 | end 25 | 26 | action_hook(:ghost, :machine_action_suspend) do |hook| 27 | hook.append(Action::RemoveHosts) 28 | end 29 | 30 | action_hook(:ghost, :machine_action_destroy) do |hook| 31 | hook.prepend(Action::CacheHosts) 32 | end 33 | 34 | action_hook(:ghost, :machine_action_destroy) do |hook| 35 | hook.append(Action::RemoveHosts) 36 | end 37 | 38 | action_hook(:ghost, :machine_action_reload) do |hook| 39 | hook.prepend(Action::RemoveHosts) 40 | hook.append(Action::UpdateHosts) 41 | end 42 | 43 | action_hook(:ghost, :machine_action_resume) do |hook| 44 | hook.append(Action::UpdateHosts) 45 | end 46 | 47 | command(:ghost, primary: true) do 48 | require_relative 'command' 49 | Command 50 | end 51 | end 52 | end 53 | end -------------------------------------------------------------------------------- /lib/vagrant-ghost/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module Ghost 3 | VERSION = "0.2.3" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /vagrant-ghost.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'vagrant-ghost/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "vagrant-ghost" 8 | spec.version = VagrantPlugins::Ghost::VERSION 9 | spec.authors = ["John P Bloch", "Eric Mann"] 10 | spec.email = ["john.bloch@10up.com","eric.mann@10up.com"] 11 | 12 | if spec.respond_to?(:metadata) 13 | spec.metadata['allowed_push_host'] = "https://rubygems.org" 14 | end 15 | 16 | spec.summary = "Update Hosts" 17 | spec.description = "Update hosts" 18 | spec.homepage = "https://github.com/10up/vagrant-ghost" 19 | spec.license = "MIT" 20 | 21 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 22 | spec.bindir = "exe" 23 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 24 | spec.require_paths = ["lib"] 25 | 26 | spec.add_development_dependency "bundler", "~> 1.7" 27 | spec.add_development_dependency "rake", "~> 10.0" 28 | end 29 | --------------------------------------------------------------------------------