Vagrant uses correct NATForwarded port for 3.2.x
This commit is contained in:
parent
7691b28c53
commit
26b837f427
|
@ -11,22 +11,10 @@ module Vagrant
|
|||
# report the collisions detected or will attempt to fix them
|
||||
# automatically if the port is configured to do so.
|
||||
def external_collision_check
|
||||
# Flatten all the already-created forwarded ports into a
|
||||
# flat list.
|
||||
used_ports = VirtualBox::VM.all.collect do |vm|
|
||||
if vm.running? && vm.uuid != runner.uuid
|
||||
vm.forwarded_ports.collect do |fp|
|
||||
fp.hostport.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
used_ports.flatten!
|
||||
used_ports.uniq!
|
||||
|
||||
existing = used_ports
|
||||
runner.env.config.vm.forwarded_ports.each do |name, options|
|
||||
if used_ports.include?(options[:hostport].to_s)
|
||||
handle_collision(name, options, used_ports)
|
||||
if existing.include?(options[:hostport].to_s)
|
||||
handle_collision(name, options, existing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -34,7 +22,7 @@ module Vagrant
|
|||
# Handles any collisions. This method will either attempt to
|
||||
# fix the collision automatically or will raise an error if
|
||||
# auto fixing is disabled.
|
||||
def handle_collision(name, options, used_ports)
|
||||
def handle_collision(name, options, existing_ports)
|
||||
if !options[:auto]
|
||||
# Auto fixing is disabled for this port forward, so we
|
||||
# must throw an error so the user can fix it.
|
||||
|
@ -46,7 +34,7 @@ module Vagrant
|
|||
# left with available ports.
|
||||
range = runner.env.config.vm.auto_port_range.to_a
|
||||
range -= runner.env.config.vm.forwarded_ports.collect { |n, o| o[:hostport].to_i }
|
||||
range -= used_ports
|
||||
range -= existing_ports
|
||||
|
||||
if range.empty?
|
||||
raise ActionException.new(:vm_port_auto_empty, :vm_name => @runner.name, :name => name, :options => options)
|
||||
|
@ -55,7 +43,7 @@ module Vagrant
|
|||
# Set the port up to be the first one and add that port to
|
||||
# the used list.
|
||||
options[:hostport] = range.shift
|
||||
used_ports << options[:hostport]
|
||||
existing_ports << options[:hostport]
|
||||
|
||||
# Notify the user
|
||||
logger.info "Fixed port collision: #{name} now on port #{options[:hostport]}"
|
||||
|
@ -67,10 +55,9 @@ module Vagrant
|
|||
end
|
||||
|
||||
def clear
|
||||
if runner.vm.forwarded_ports.length > 0
|
||||
if used_ports.length > 0
|
||||
logger.info "Deleting any previously set forwarded ports..."
|
||||
fp = runner.vm.forwarded_ports.dup
|
||||
fp.collect { |p| p.destroy }
|
||||
clear_ports
|
||||
runner.reload!
|
||||
end
|
||||
end
|
||||
|
@ -78,28 +65,90 @@ module Vagrant
|
|||
def forward_ports
|
||||
logger.info "Forwarding ports..."
|
||||
|
||||
@runner.env.config.vm.forwarded_ports.each do |name, options|
|
||||
runner.env.config.vm.forwarded_ports.each do |name, options|
|
||||
adapter = options[:adapter]
|
||||
|
||||
# Assuming the only reason to establish port forwarding is because the VM is using Virtualbox NAT networking.
|
||||
# Host-only or Bridged networking don't require port-forwarding and establishing forwarded ports on these
|
||||
# attachment types has uncertain behaviour.
|
||||
if @runner.vm.network_adapters[adapter].attachment_type == :nat
|
||||
logger.info "Forwarding \"#{name}\": #{options[:guestport]} on adapter \##{adapter+1} => #{options[:hostport]}"
|
||||
port = VirtualBox::ForwardedPort.new
|
||||
port.name = name
|
||||
port.hostport = options[:hostport]
|
||||
port.guestport = options[:guestport]
|
||||
port.instance = adapter
|
||||
@runner.vm.forwarded_ports << port
|
||||
logger.info "Forwarding \"#{name}\": #{options[:guestport]} on adapter \##{adapter+1} => #{options[:hostport]}"
|
||||
forward_port(name, options)
|
||||
else
|
||||
logger.info "VirtualBox adapter \##{adapter+1} not configured as \"NAT\"."
|
||||
logger.info "Skipped port forwarding \"#{name}\": #{options[:guestport]} on adapter\##{adapter+1} => #{options[:hostport]}"
|
||||
end
|
||||
end
|
||||
|
||||
@runner.vm.save
|
||||
@runner.reload!
|
||||
runner.vm.save
|
||||
runner.reload!
|
||||
end
|
||||
|
||||
#------------------------------------------------------------
|
||||
# VirtualBox version-specific helpers below. VirtualBox 3.2
|
||||
# introduced a breaking change into the way that forwarded
|
||||
# ports are handled. For backwards compatability with 3.1, we
|
||||
# need this trickery.
|
||||
#------------------------------------------------------------
|
||||
# TODO In a future version, fix this.
|
||||
|
||||
# Returns an array of used ports. This method is implemented
|
||||
# differently depending on the VirtualBox version, but the
|
||||
# behavior is the same.
|
||||
#
|
||||
# @return [Array<String>]
|
||||
def used_ports
|
||||
result = VirtualBox::VM.all.collect do |vm|
|
||||
if vm.running? && vm.uuid != runner.uuid
|
||||
if VirtualBox.version =~ /^3\.1\./
|
||||
# VirtualBox 3.1.x uses forwarded ports via extra-data
|
||||
vm.forwarded_ports.collect do |fp|
|
||||
fp.hostport.to_s
|
||||
end
|
||||
else
|
||||
# VirtualBox 3.2.x uses forwarded ports via NAT engines
|
||||
vm.network_adapters.collect do |na|
|
||||
na.nat_engine.forwarded_ports.collect do |fp|
|
||||
fp.hostport.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
result.flatten.uniq
|
||||
end
|
||||
|
||||
# Deletes existing forwarded ports.
|
||||
def clear_ports
|
||||
if VirtualBox.version =~ /^3\.1\./
|
||||
fp = runner.vm.forwarded_ports.dup
|
||||
fp.each { |p| p.destroy }
|
||||
else
|
||||
runner.vm.network_adapters.each do |na|
|
||||
na.nat_engine.forwarded_ports.dup.each do |fp|
|
||||
fp.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Forwards a port.
|
||||
def forward_port(name, options)
|
||||
if VirtualBox.version =~ /^3\.1\./
|
||||
port = VirtualBox::ForwardedPort.new
|
||||
port.name = name
|
||||
port.hostport = options[:hostport]
|
||||
port.guestport = options[:guestport]
|
||||
port.instance = options[:adapter]
|
||||
runner.vm.forwarded_ports << port
|
||||
else
|
||||
port = VirtualBox::NATForwardedPort.new
|
||||
port.name = name
|
||||
port.guestport = options[:guestport]
|
||||
port.hostport = options[:hostport]
|
||||
runner.vm.network_adapters[options[:adapter]].nat_engine.forwarded_ports << port
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,18 +15,6 @@ class ForwardPortsActionTest < Test::Unit::TestCase
|
|||
|
||||
context "checking for colliding external ports" do
|
||||
setup do
|
||||
@forwarded_port = mock("forwarded_port")
|
||||
@forwarded_port.stubs(:hostport)
|
||||
@forwarded_ports = [@forwarded_port]
|
||||
|
||||
@vm = mock("vm")
|
||||
@vm.stubs(:forwarded_ports).returns(@forwarded_ports)
|
||||
@vm.stubs(:running?).returns(true)
|
||||
@vm.stubs(:uuid).returns("foo")
|
||||
@runner.stubs(:uuid).returns("bar")
|
||||
vms = [@vm]
|
||||
VirtualBox::VM.stubs(:all).returns(vms)
|
||||
|
||||
@env = mock_environment do |config|
|
||||
config.vm.forwarded_ports.clear
|
||||
config.vm.forward_port("ssh", 22, 2222)
|
||||
|
@ -34,29 +22,20 @@ class ForwardPortsActionTest < Test::Unit::TestCase
|
|||
|
||||
@runner.stubs(:env).returns(@env)
|
||||
|
||||
@used_ports = []
|
||||
@action.stubs(:used_ports).returns(@used_ports)
|
||||
|
||||
# So no exceptions are raised
|
||||
@action.stubs(:handle_collision)
|
||||
end
|
||||
|
||||
should "ignore vms which aren't running" do
|
||||
@vm.expects(:running?).returns(false)
|
||||
@vm.expects(:forwarded_ports).never
|
||||
@action.external_collision_check
|
||||
end
|
||||
|
||||
should "ignore vms which are equivalent to ours" do
|
||||
@runner.expects(:uuid).returns(@vm.uuid)
|
||||
@vm.expects(:forwarded_ports).never
|
||||
@action.external_collision_check
|
||||
end
|
||||
|
||||
should "not raise any errors if no forwarded ports collide" do
|
||||
@forwarded_port.expects(:hostport).returns(80)
|
||||
@used_ports << "80"
|
||||
assert_nothing_raised { @action.external_collision_check }
|
||||
end
|
||||
|
||||
should "handle the collision if it happens" do
|
||||
@forwarded_port.expects(:hostport).returns(2222)
|
||||
@used_ports << "2222"
|
||||
@action.expects(:handle_collision).with("ssh", anything, anything).once
|
||||
@action.external_collision_check
|
||||
end
|
||||
|
@ -127,24 +106,11 @@ class ForwardPortsActionTest < Test::Unit::TestCase
|
|||
@vm.expects(:network_adapters).returns([network_adapter])
|
||||
network_adapter.expects(:attachment_type).returns(:nat)
|
||||
|
||||
@runner.env.config.vm.forwarded_ports.each do |name, opts|
|
||||
forwarded_ports.expects(:<<).with do |port|
|
||||
assert_equal name, port.name
|
||||
assert_equal opts[:hostport], port.hostport
|
||||
assert_equal opts[:guestport], port.guestport
|
||||
assert_equal opts[:adapter], port.instance
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
@vm.expects(:forwarded_ports).returns(forwarded_ports)
|
||||
@vm.expects(:save).once
|
||||
@runner.expects(:reload!).once
|
||||
@action.forward_ports
|
||||
end
|
||||
end
|
||||
|
||||
context "Not forwarding ports" do
|
||||
should "No port forwarding for non NAT interfaces" do
|
||||
forwarded_ports = mock("forwarded_ports")
|
||||
network_adapter = mock("network_adapter")
|
||||
|
@ -158,23 +124,176 @@ class ForwardPortsActionTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
context "clearing forwarded ports" do
|
||||
should "call destroy on all forwarded ports" do
|
||||
forwarded_ports = []
|
||||
5.times do |i|
|
||||
port = mock("port#{i}")
|
||||
port.expects(:destroy).once
|
||||
forwarded_ports << port
|
||||
end
|
||||
setup do
|
||||
@action.stubs(:used_ports).returns([:a])
|
||||
@action.stubs(:clear_ports)
|
||||
end
|
||||
|
||||
@vm.stubs(:forwarded_ports).returns(forwarded_ports)
|
||||
should "call destroy on all forwarded ports" do
|
||||
@action.expects(:clear_ports).once
|
||||
@runner.expects(:reload!)
|
||||
@action.clear
|
||||
end
|
||||
|
||||
should "do nothing if there are no forwarded ports" do
|
||||
@vm.stubs(:forwarded_ports).returns([])
|
||||
@action.stubs(:used_ports).returns([])
|
||||
@runner.expects(:reload!).never
|
||||
@action.clear
|
||||
end
|
||||
end
|
||||
|
||||
context "getting list of used ports" do
|
||||
setup do
|
||||
@vms = []
|
||||
VirtualBox::VM.stubs(:all).returns(@vms)
|
||||
VirtualBox.stubs(:version).returns("3.1.0")
|
||||
@runner.stubs(:uuid).returns(:bar)
|
||||
end
|
||||
|
||||
def mock_vm(options={})
|
||||
options = {
|
||||
:running? => true,
|
||||
:uuid => :foo
|
||||
}.merge(options)
|
||||
|
||||
vm = mock("vm")
|
||||
options.each do |k,v|
|
||||
vm.stubs(k).returns(v)
|
||||
end
|
||||
|
||||
vm
|
||||
end
|
||||
|
||||
def mock_fp(hostport)
|
||||
fp = mock("fp")
|
||||
fp.stubs(:hostport).returns(hostport.to_s)
|
||||
fp
|
||||
end
|
||||
|
||||
should "ignore VMs which aren't running" do
|
||||
@vms << mock_vm(:running? => false)
|
||||
@vms[0].expects(:forwarded_ports).never
|
||||
@action.used_ports
|
||||
end
|
||||
|
||||
should "ignore VMs of the same uuid" do
|
||||
@vms << mock_vm(:uuid => @runner.uuid)
|
||||
@vms[0].expects(:forwarded_ports).never
|
||||
@action.used_ports
|
||||
end
|
||||
|
||||
should "return the forwarded ports for VB 3.1.x" do
|
||||
VirtualBox.stubs(:version).returns("3.1.4")
|
||||
fps = [mock_fp(2222), mock_fp(80)]
|
||||
@vms << mock_vm(:forwarded_ports => fps)
|
||||
assert_equal %W[2222 80], @action.used_ports
|
||||
end
|
||||
|
||||
should "return the forwarded ports for VB 3.2.x" do
|
||||
VirtualBox.stubs(:version).returns("3.2.4")
|
||||
fps = [mock_fp(2222), mock_fp(80)]
|
||||
na = mock("na")
|
||||
ne = mock("ne")
|
||||
na.stubs(:nat_engine).returns(ne)
|
||||
ne.stubs(:forwarded_ports).returns(fps)
|
||||
@vms << mock_vm(:network_adapters => [na])
|
||||
assert_equal %W[2222 80], @action.used_ports
|
||||
end
|
||||
end
|
||||
|
||||
context "clearing ports" do
|
||||
def mock_fp
|
||||
fp = mock("fp")
|
||||
fp.expects(:destroy).once
|
||||
fp
|
||||
end
|
||||
|
||||
context "in VB 3.1.x" do
|
||||
setup do
|
||||
VirtualBox.stubs(:version).returns("3.1.4")
|
||||
@fps = []
|
||||
@vm.stubs(:forwarded_ports).returns(@fps)
|
||||
end
|
||||
|
||||
should "destroy each forwarded port" do
|
||||
@fps << mock_fp
|
||||
@fps << mock_fp
|
||||
@action.clear_ports
|
||||
end
|
||||
end
|
||||
|
||||
context "in VB 3.2.x" do
|
||||
setup do
|
||||
VirtualBox.stubs(:version).returns("3.2.8")
|
||||
@adapters = []
|
||||
@vm.stubs(:network_adapters).returns(@adapters)
|
||||
end
|
||||
|
||||
def mock_adapter
|
||||
na = mock("adapter")
|
||||
engine = mock("engine")
|
||||
engine.stubs(:forwarded_ports).returns([mock_fp])
|
||||
na.stubs(:nat_engine).returns(engine)
|
||||
na
|
||||
end
|
||||
|
||||
should "destroy each forwarded port" do
|
||||
@adapters << mock_adapter
|
||||
@adapters << mock_adapter
|
||||
@action.clear_ports
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "forwarding ports" do
|
||||
context "in VB 3.1.x" do
|
||||
setup do
|
||||
VirtualBox.stubs(:version).returns("3.1.4")
|
||||
end
|
||||
|
||||
should "forward ports" do
|
||||
forwarded_ports = mock("forwarded_ports")
|
||||
@vm.expects(:forwarded_ports).returns(forwarded_ports)
|
||||
@runner.env.config.vm.forwarded_ports.each do |name, opts|
|
||||
forwarded_ports.expects(:<<).with do |port|
|
||||
assert_equal name, port.name
|
||||
assert_equal opts[:hostport], port.hostport
|
||||
assert_equal opts[:guestport], port.guestport
|
||||
assert_equal opts[:adapter], port.instance
|
||||
true
|
||||
end
|
||||
|
||||
@action.forward_port(name, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "in VB 3.2.x" do
|
||||
setup do
|
||||
VirtualBox.stubs(:version).returns("3.2.8")
|
||||
end
|
||||
|
||||
should "forward ports" do
|
||||
name, opts = @runner.env.config.vm.forwarded_ports.first
|
||||
|
||||
adapters = []
|
||||
adapter = mock("adapter")
|
||||
engine = mock("engine")
|
||||
fps = mock("forwarded ports")
|
||||
adapter.stubs(:nat_engine).returns(engine)
|
||||
engine.stubs(:forwarded_ports).returns(fps)
|
||||
fps.expects(:<<).with do |port|
|
||||
assert_equal name, port.name
|
||||
assert_equal opts[:hostport], port.hostport
|
||||
assert_equal opts[:guestport], port.guestport
|
||||
true
|
||||
end
|
||||
|
||||
adapters[opts[:adapter]] = adapter
|
||||
@vm.stubs(:network_adapters).returns(adapters)
|
||||
|
||||
@action.forward_port(name, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,6 +53,7 @@ class CommandsSSHTest < Test::Unit::TestCase
|
|||
@vm = mock("vm")
|
||||
@vm.stubs(:created?).returns(true)
|
||||
@vm.stubs(:ssh).returns(@ssh)
|
||||
@vm.stubs(:env).returns(@env)
|
||||
end
|
||||
|
||||
should "error and exit if invalid VM given" do
|
||||
|
|
Loading…
Reference in New Issue