This branch brings in the "machine abstraction" code. This is a major
milestone in the development of Vagrant as it abstracts all of the
VirtualBox-specific code out into a plugin. There is zero VirtualBox
specific code in the core ("lib/") directory at this point. Read on for
important points.
== Gotchas
White it is technically possible now to write plugins for other
providers, there is still major work to be done to make this feasible.
The plugin interface itself is pretty much done, but there are some
issues:
* ":virtualbox" is the hardcoded provider to be used at the moment.
* There is no way to configure a provider. For example,
`config.vm.customize` would never work for anything other than
VirtualBox, so there needs to be a way to have provider-specific
configuration. This will come soon.
* Shared folders and networking need to be rearchitected to be friendly
for multiple providers, since it is unrealistic that a provider such as
EC2 could provide the same level of networking, for example.
* There is no way easy way (like `vagrant package --base`) to create
boxes for providers other than VirtualBox. This will be addressed in a
whole new feature of Vagrant probably in a future release after
provider stuff has shipped.
== Writing a Provider
To write a provider, you create a Vagrant plugin that defines a
"provider". See the "plugins/providers/virtualbox/plugin.rb" for more
details. Providers themselves have an exremely simple API. The burden
for writing providers mostly rests on the fact that you must define
complex middleware sequences.
Lots more work to come in the future, but this is a BIG MILESTONE!
90 lines
3.2 KiB
Ruby
90 lines
3.2 KiB
Ruby
require "vagrant/util/is_port_open"
|
|
|
|
module VagrantPlugins
|
|
module ProviderVirtualBox
|
|
module Action
|
|
class CheckPortCollisions
|
|
include Vagrant::Util::IsPortOpen
|
|
|
|
def initialize(app, env)
|
|
@app = app
|
|
end
|
|
|
|
def call(env)
|
|
# For the handlers...
|
|
@env = env
|
|
|
|
# Figure out how we handle port collisions. By default we error.
|
|
handler = env[:port_collision_handler] || :error
|
|
|
|
# Read our forwarded ports, if we have any, to override what
|
|
# we have configured.
|
|
current = {}
|
|
env[:machine].provider.driver.read_forwarded_ports.each do |nic, name, hostport, guestport|
|
|
current[name] = hostport.to_i
|
|
end
|
|
|
|
existing = env[:machine].provider.driver.read_used_ports
|
|
env[:machine].config.vm.forwarded_ports.each do |options|
|
|
# Use the proper port, whether that be the configured port or the
|
|
# port that is currently on use of the VM.
|
|
hostport = options[:hostport].to_i
|
|
hostport = current[options[:name]] if current.has_key?(options[:name])
|
|
|
|
if existing.include?(hostport) || is_port_open?("127.0.0.1", hostport)
|
|
# We have a collision! Handle it
|
|
send("handle_#{handler}".to_sym, options, existing)
|
|
end
|
|
end
|
|
|
|
@app.call(env)
|
|
end
|
|
|
|
# Handles a port collision by raising an exception.
|
|
def handle_error(options, existing_ports)
|
|
raise Vagrant::Errors::ForwardPortCollisionResume
|
|
end
|
|
|
|
# Handles a port collision by attempting to fix it.
|
|
def handle_correct(options, existing_ports)
|
|
# We need to keep this for messaging purposes
|
|
original_hostport = options[:hostport]
|
|
|
|
if !options[:auto]
|
|
# Auto fixing is disabled for this port forward, so we
|
|
# must throw an error so the user can fix it.
|
|
raise Vagrant::Errors::ForwardPortCollision,
|
|
:host_port => options[:hostport].to_s,
|
|
:guest_port => options[:guestport].to_s
|
|
end
|
|
|
|
# Get the auto port range and get rid of the used ports and
|
|
# ports which are being used in other forwards so we're just
|
|
# left with available ports.
|
|
range = @env[:machine].config.vm.auto_port_range.to_a
|
|
range -= @env[:machine].config.vm.forwarded_ports.collect { |opts| opts[:hostport].to_i }
|
|
range -= existing_ports
|
|
|
|
if range.empty?
|
|
raise Vagrant::Errors::ForwardPortAutolistEmpty,
|
|
:vm_name => @env[:machine].name,
|
|
:host_port => options[:hostport].to_s,
|
|
:guest_port => options[:guestport].to_s
|
|
end
|
|
|
|
# Set the port up to be the first one and add that port to
|
|
# the used list.
|
|
options[:hostport] = range.shift
|
|
existing_ports << options[:hostport]
|
|
|
|
# Notify the user
|
|
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision",
|
|
:host_port => original_hostport.to_s,
|
|
:guest_port => options[:guestport].to_s,
|
|
:new_port => options[:hostport]))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|