pleschev-vagrant-hostmaster 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|