pleschev-vagrant-hostmaster 0.8.1
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.
- data/.gitignore +21 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +108 -0
- data/Rakefile +11 -0
- data/lib/vagrant/hostmaster.rb +15 -0
- data/lib/vagrant/hostmaster/command/base.rb +32 -0
- data/lib/vagrant/hostmaster/command/list.rb +8 -0
- data/lib/vagrant/hostmaster/command/remove.rb +8 -0
- data/lib/vagrant/hostmaster/command/root.rb +68 -0
- data/lib/vagrant/hostmaster/command/update.rb +8 -0
- data/lib/vagrant/hostmaster/config.rb +11 -0
- data/lib/vagrant/hostmaster/middleware/remove.rb +21 -0
- data/lib/vagrant/hostmaster/middleware/update.rb +21 -0
- data/lib/vagrant/hostmaster/version.rb +5 -0
- data/lib/vagrant/hostmaster/vm.rb +165 -0
- data/lib/vagrant_init.rb +1 -0
- data/test/vagrant/hostmaster/command_tests/list_test.rb +43 -0
- data/test/vagrant/hostmaster/command_tests/remove_test.rb +43 -0
- data/test/vagrant/hostmaster/command_tests/root_test.rb +49 -0
- data/test/vagrant/hostmaster/command_tests/update_test.rb +43 -0
- data/test/vagrant/hostmaster/config_test.rb +29 -0
- data/test/vagrant/hostmaster/middleware_tests/remove_test.rb +40 -0
- data/test/vagrant/hostmaster/middleware_tests/update_test.rb +38 -0
- data/test/vagrant/hostmaster/multiple_vm_test.rb +88 -0
- data/test/vagrant/hostmaster/simple_vm_test.rb +78 -0
- data/test/vagrant/hostmaster/test_helpers.rb +137 -0
- data/test/vagrant/hostmaster/ui/capture.rb +35 -0
- data/vagrant-hostmaster.gemspec +22 -0
- metadata +163 -0
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
.DS_Store
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
*.swo
|
5
|
+
*.swp
|
6
|
+
.bundle
|
7
|
+
.config
|
8
|
+
.yardoc
|
9
|
+
Gemfile.lock
|
10
|
+
InstalledFiles
|
11
|
+
_yardoc
|
12
|
+
coverage
|
13
|
+
doc/
|
14
|
+
lib/bundler/man
|
15
|
+
pkg
|
16
|
+
rdoc
|
17
|
+
spec/reports
|
18
|
+
test/tmp
|
19
|
+
test/version_tmp
|
20
|
+
tmp
|
21
|
+
vendor/bundle
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 S. Brent Faulkner
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# vagrant-hostmaster
|
2
|
+
|
3
|
+
`vagrant-hostmaster` is a Vagrant plugin to manage /etc/hosts entries on both the host OS and guest VMs.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install into vagrant's isolated RubyGems instance using:
|
8
|
+
|
9
|
+
$ vagrant gem install vagrant-hostmaster
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
vagrant-hostmaster will automatically add/update /etc/hosts entries when you run `vagrant up`
|
14
|
+
(or `vagrant provision`).
|
15
|
+
|
16
|
+
The host entries will automatically be removed when you run `vagrant destroy`.
|
17
|
+
|
18
|
+
### Configuration
|
19
|
+
|
20
|
+
By default, the name specified in the `vm.host_name` configuration option will be associated
|
21
|
+
with the address of the first network interface.
|
22
|
+
|
23
|
+
For example:
|
24
|
+
|
25
|
+
Vagrant::Config.run do |config|
|
26
|
+
...
|
27
|
+
|
28
|
+
config.vm.host_name = "www.example.com"
|
29
|
+
|
30
|
+
config.vm.network :hostonly, "33.33.33.60"
|
31
|
+
end
|
32
|
+
|
33
|
+
This would result in the following hosts entry:
|
34
|
+
|
35
|
+
33.33.33.60 www.example.com # VAGRANT: ...
|
36
|
+
|
37
|
+
### Using a different host name
|
38
|
+
|
39
|
+
To use a different host name, specify it in your Vagrantfile using the `hosts.name` configuration
|
40
|
+
option.
|
41
|
+
|
42
|
+
For example:
|
43
|
+
|
44
|
+
Vagrant::Config.run do |config|
|
45
|
+
...
|
46
|
+
|
47
|
+
config.vm.host_name = "www.example.com"
|
48
|
+
config.hosts.name = "example.com"
|
49
|
+
|
50
|
+
config.vm.network :hostonly, "33.33.33.60"
|
51
|
+
end
|
52
|
+
|
53
|
+
This would result in the following hosts entry:
|
54
|
+
|
55
|
+
33.33.33.60 example.com # VAGRANT: ...
|
56
|
+
|
57
|
+
### Host aliases
|
58
|
+
|
59
|
+
In addition, the `hosts.aliases` configuration option can be used to provide aliases for your host names.
|
60
|
+
|
61
|
+
For example:
|
62
|
+
|
63
|
+
Vagrant::Config.run do |config|
|
64
|
+
...
|
65
|
+
|
66
|
+
config.vm.host_name = "www.example.com"
|
67
|
+
config.hosts.aliases = %w(example.com)
|
68
|
+
|
69
|
+
config.vm.network :hostonly, "33.33.33.60"
|
70
|
+
end
|
71
|
+
|
72
|
+
This would result in the following hosts entry:
|
73
|
+
|
74
|
+
33.33.33.60 www.example.com example.com # VAGRANT: ...
|
75
|
+
|
76
|
+
## Command Line
|
77
|
+
|
78
|
+
In addition to automatically updating the hosts file, the `hosts` command supports manual
|
79
|
+
modification (or verification) of the hosts entries.
|
80
|
+
|
81
|
+
Usage: vagrant hosts <command> [<args>]
|
82
|
+
|
83
|
+
Available subcommands:
|
84
|
+
list
|
85
|
+
remove
|
86
|
+
update
|
87
|
+
|
88
|
+
For help on any individual command run `vagrant hosts COMMAND -h`
|
89
|
+
|
90
|
+
### List Host Entries
|
91
|
+
|
92
|
+
vagrant hosts list [vm-name]
|
93
|
+
|
94
|
+
### Remove Host Entries
|
95
|
+
|
96
|
+
vagrant hosts remove [vm-name]
|
97
|
+
|
98
|
+
### Update Host Entries
|
99
|
+
|
100
|
+
vagrant hosts update [vm-name]
|
101
|
+
|
102
|
+
## Contributing
|
103
|
+
|
104
|
+
1. Fork it
|
105
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
106
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
107
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
108
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
require 'vagrant/hostmaster/command/base'
|
3
|
+
require 'vagrant/hostmaster/command/root'
|
4
|
+
require 'vagrant/hostmaster/config'
|
5
|
+
require 'vagrant/hostmaster/vm'
|
6
|
+
require 'vagrant/hostmaster/middleware/remove'
|
7
|
+
require 'vagrant/hostmaster/middleware/update'
|
8
|
+
|
9
|
+
Vagrant.commands.register(:hosts) { Vagrant::Hostmaster::Command::Root }
|
10
|
+
Vagrant.config_keys.register(:hosts) { Vagrant::Hostmaster::Config }
|
11
|
+
|
12
|
+
Vagrant.actions[:destroy].insert_after(Vagrant::Action::VM::ProvisionerCleanup, Vagrant::Hostmaster::Middleware::Remove)
|
13
|
+
|
14
|
+
Vagrant.actions[:provision].insert_after(Vagrant::Action::VM::Provision, Vagrant::Hostmaster::Middleware::Update)
|
15
|
+
Vagrant.actions[:start].insert_after(Vagrant::Action::VM::Provision, Vagrant::Hostmaster::Middleware::Update)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Hostmaster
|
3
|
+
module Command
|
4
|
+
class Base < Vagrant::Command::Base
|
5
|
+
def execute
|
6
|
+
sub_command = self.class.name.split('::').last.downcase
|
7
|
+
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: vagrant hosts #{sub_command} [vm-name]"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Parse the options
|
13
|
+
argv = parse_options(parser)
|
14
|
+
return if !argv
|
15
|
+
|
16
|
+
with_target_vms(argv) do |vm|
|
17
|
+
if vm.state == :running
|
18
|
+
Hostmaster::VM.new(vm).send sub_command.to_sym
|
19
|
+
elsif vm.created?
|
20
|
+
vm.ui.info I18n.t("vagrant.commands.common.vm_not_running")
|
21
|
+
else
|
22
|
+
vm.ui.info I18n.t("vagrant.commands.common.vm_not_created")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Success, exit status 0
|
27
|
+
0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Hostmaster
|
3
|
+
module Command
|
4
|
+
class Root < Vagrant::Command::Base
|
5
|
+
def initialize(argv, env)
|
6
|
+
super
|
7
|
+
|
8
|
+
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
|
9
|
+
|
10
|
+
@subcommands = Vagrant::Registry.new
|
11
|
+
@subcommands.register(:list) do
|
12
|
+
require File.expand_path("../list", __FILE__)
|
13
|
+
List
|
14
|
+
end
|
15
|
+
|
16
|
+
@subcommands.register(:remove) do
|
17
|
+
require File.expand_path("../remove", __FILE__)
|
18
|
+
Remove
|
19
|
+
end
|
20
|
+
|
21
|
+
@subcommands.register(:update) do
|
22
|
+
require File.expand_path("../update", __FILE__)
|
23
|
+
Update
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute
|
28
|
+
if @main_args.include?("-h") || @main_args.include?("--help")
|
29
|
+
# Print the help for all the hosts commands.
|
30
|
+
return help
|
31
|
+
end
|
32
|
+
|
33
|
+
# If we reached this far then we must have a subcommand. If not,
|
34
|
+
# then we also just print the help and exit.
|
35
|
+
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
|
36
|
+
return help if !command_class || !@sub_command
|
37
|
+
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
|
38
|
+
|
39
|
+
# Initialize and execute the command class
|
40
|
+
command_class.new(@sub_args, @env).execute
|
41
|
+
end
|
42
|
+
|
43
|
+
# Prints the help out for this command
|
44
|
+
def help
|
45
|
+
opts = OptionParser.new do |opts|
|
46
|
+
opts.banner = "Usage: vagrant hosts <command> [<args>]"
|
47
|
+
opts.separator ""
|
48
|
+
opts.separator "Available subcommands:"
|
49
|
+
|
50
|
+
# Add the available subcommands as separators in order to print them
|
51
|
+
# out as well.
|
52
|
+
keys = []
|
53
|
+
@subcommands.each { |key, value| keys << key.to_s }
|
54
|
+
|
55
|
+
keys.sort.each do |key|
|
56
|
+
opts.separator " #{key}"
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.separator ""
|
60
|
+
opts.separator "For help on any individual command run `vagrant hosts COMMAND -h`"
|
61
|
+
end
|
62
|
+
|
63
|
+
@env.ui.info(opts.help, :prefix => false)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Hostmaster
|
3
|
+
module Middleware
|
4
|
+
class Remove
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
remove env[:vm] if env["vm"].created?
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
def remove(vm)
|
16
|
+
Hostmaster::VM.new(vm).remove(:guests => false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module Hostmaster
|
3
|
+
module Middleware
|
4
|
+
class Update
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
@app.call(env)
|
11
|
+
update env[:vm] if env["vm"].state == :running
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
def update(vm)
|
16
|
+
Hostmaster::VM.new(vm).update
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Vagrant
|
4
|
+
module Hostmaster
|
5
|
+
class VM
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@vm, :channel, :config, :env, :name, :uuid
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def expand_path(relative_path, relative_to)
|
12
|
+
File.expand_path(relative_path, relative_to)
|
13
|
+
end
|
14
|
+
|
15
|
+
def hosts_path
|
16
|
+
Util::Platform.windows? ? expand_path('system32/drivers/etc/hosts', ENV['SYSTEMROOT']) : '/etc/hosts'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(vm)
|
21
|
+
@vm = vm
|
22
|
+
end
|
23
|
+
|
24
|
+
def add(options = {})
|
25
|
+
options.merge! config_to_h
|
26
|
+
if process_local?(options)
|
27
|
+
env.ui.info("Adding host entry for #{name} VM. Administrator privileges will be required...") unless options[:quiet]
|
28
|
+
sudo add_command
|
29
|
+
end
|
30
|
+
|
31
|
+
with_other_vms { |vm| channel.sudo vm.add_command(:uuid => uuid, :hosts_path => hosts_path) } if process_guests?(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def hosts_path
|
35
|
+
# TODO: if windows guests are supported, this will need to be smarter
|
36
|
+
"/etc/hosts"
|
37
|
+
end
|
38
|
+
|
39
|
+
def list(options = {})
|
40
|
+
options.merge! config_to_h
|
41
|
+
if process_local?(options)
|
42
|
+
output = `#{list_command}`.chomp
|
43
|
+
env.ui.info("[local] #{output}\n\n", :prefix => false) unless output.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
if process_guests?(options)
|
47
|
+
entries = ""
|
48
|
+
with_other_vms do |vm|
|
49
|
+
channel.execute(vm.list_command(:uuid => uuid, :hosts_path => hosts_path), :error_check => false) do |type, data|
|
50
|
+
entries << data if type == :stdout
|
51
|
+
end
|
52
|
+
end
|
53
|
+
entries = entries.split($/).collect { |entry| "[#{name}] #{entry}" }.join("\n")
|
54
|
+
env.ui.info("#{entries}\n\n", :prefix => false) unless entries.empty?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove(options = {})
|
59
|
+
options.merge! config_to_h
|
60
|
+
if process_local?(options)
|
61
|
+
env.ui.info("Removing host entry for #{name} VM. Administrator privileges will be required...") unless options[:quiet]
|
62
|
+
sudo remove_command
|
63
|
+
end
|
64
|
+
with_other_vms { |vm| channel.sudo vm.remove_command(:uuid => uuid, :hosts_path => hosts_path) } if process_guests?(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def update(options = {})
|
68
|
+
options.merge! config_to_h
|
69
|
+
if process_local?(options)
|
70
|
+
env.ui.info("Updating host entry for #{name} VM. Administrator privileges will be required...") unless options[:quiet]
|
71
|
+
sudo(remove_command) && sudo(add_command)
|
72
|
+
end
|
73
|
+
with_other_vms { |vm| channel.sudo(vm.remove_command(:uuid => uuid, :hosts_path => hosts_path)) && channel.sudo(vm.add_command(:uuid => uuid, :hosts_path => hosts_path)) } if process_guests?(options)
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def add_command(options = {})
|
78
|
+
uuid = options[:uuid] || self.uuid
|
79
|
+
hosts_path = options[:hosts_path] || self.class.hosts_path
|
80
|
+
%Q(sh -c 'echo "#{host_entry(uuid)}" >>#{hosts_path}')
|
81
|
+
end
|
82
|
+
|
83
|
+
def address
|
84
|
+
# network parameters consist of an address and a hash of options
|
85
|
+
@address ||= (network_parameters && network_parameters.first)
|
86
|
+
end
|
87
|
+
|
88
|
+
def host_aliases
|
89
|
+
@host_aliases ||= Array(config.hosts.aliases)
|
90
|
+
end
|
91
|
+
|
92
|
+
def host_entry(uuid = self.uuid)
|
93
|
+
%Q(#{address} #{host_names.join(' ')} #{signature(uuid)})
|
94
|
+
end
|
95
|
+
|
96
|
+
def host_name
|
97
|
+
@host_name ||= (config.hosts.name || config.vm.host_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def host_names
|
101
|
+
@host_names ||= (Array(host_name) + host_aliases)
|
102
|
+
end
|
103
|
+
|
104
|
+
def process_guests?(options = {})
|
105
|
+
{:guests => true}.merge(options)[:guests]
|
106
|
+
end
|
107
|
+
|
108
|
+
def process_local?(options = {})
|
109
|
+
{:local => true}.merge(options)[:local]
|
110
|
+
end
|
111
|
+
|
112
|
+
def list_command(options = {})
|
113
|
+
uuid = options[:uuid] || self.uuid
|
114
|
+
hosts_path = options[:hosts_path] || self.class.hosts_path
|
115
|
+
%Q(grep '#{signature(uuid)}$' #{hosts_path})
|
116
|
+
end
|
117
|
+
|
118
|
+
def network
|
119
|
+
# hostonly networks are the only ones we're interested in
|
120
|
+
@network ||= networks.find { |type,network_parameters| type == :hostonly }
|
121
|
+
end
|
122
|
+
|
123
|
+
def network_parameters
|
124
|
+
# network is a pair of a network type and the network parameters
|
125
|
+
@network_parameters ||= (network && network.last)
|
126
|
+
end
|
127
|
+
|
128
|
+
def networks
|
129
|
+
@networks ||= config.vm.networks
|
130
|
+
end
|
131
|
+
|
132
|
+
def remove_command(options = {})
|
133
|
+
uuid = options[:uuid] || self.uuid
|
134
|
+
hosts_path = options[:hosts_path] || self.class.hosts_path
|
135
|
+
%Q(sed -e '/#{signature(uuid)}$/ d' -ibak #{hosts_path})
|
136
|
+
end
|
137
|
+
|
138
|
+
def signature(uuid = self.uuid)
|
139
|
+
%Q(# VAGRANT: #{uuid} (#{name}))
|
140
|
+
end
|
141
|
+
|
142
|
+
def sudo(command)
|
143
|
+
if Util::Platform.windows?
|
144
|
+
`#{command}`
|
145
|
+
else
|
146
|
+
`sudo #{command}`
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def with_other_vms
|
151
|
+
env.vms.each do |name,vm|
|
152
|
+
yield Hostmaster::VM.new(vm) if vm.config.vm.networks.any? { |type,network_parameters| type == :hostonly } && vm.name != self.name && vm.state == :running
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def config_to_h
|
157
|
+
{
|
158
|
+
:local => config.hosts.local.nil? ? true : config.hosts.local,
|
159
|
+
:guests => config.hosts.guests.nil? ? true : config.hosts.guests,
|
160
|
+
:quiet => config.hosts.quiet.nil? ? false : config.hosts.quiet
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|