Class: Vagrant::Action::Builtin::HandleForwardedPortCollisions

Inherits:
Object
  • Object
show all
Includes:
Util::IsPortOpen
Defined in:
lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb

Overview

This middleware class will detect and handle collisions with forwarded ports, whether that means raising an error or repairing them automatically.

Parameters it takes from the environment hash:

* `:port_collision_repair` - If true, it will attempt to repair
  port collisions. If false, it will raise an exception when
  there is a collision.

* `:port_collision_extra_in_use` - An array of ports that are
  considered in use.

* `:port_collision_remap` - A hash remapping certain host ports
  to other host ports.

Instance Method Summary collapse

Methods included from Util::IsPortOpen

#is_port_open?

Constructor Details

#initialize(app, env) ⇒ HandleForwardedPortCollisions

Returns a new instance of HandleForwardedPortCollisions.



29
30
31
32
# File 'lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb', line 29

def initialize(app, env)
  @app    = app
  @logger = Log4r::Logger.new("vagrant::action::builtin::handle_port_collisions")
end

Instance Method Details

#call(env) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb', line 34

def call(env)
  @logger.info("Detecting any forwarded port collisions...")

  # Get the extra ports we consider in use
  extra_in_use = env[:port_collision_extra_in_use] || []

  # Get the remap
  remap = env[:port_collision_remap] || {}

  # Determine the handler we'll use if we have any port collisions
  repair = !!env[:port_collision_repair]

  # Log out some of our parameters
  @logger.debug("Extra in use: #{extra_in_use.inspect}")
  @logger.debug("Remap: #{remap.inspect}")
  @logger.debug("Repair: #{repair.inspect}")

  # Determine a list of usable ports for repair
  usable_ports = Set.new(env[:machine].config.vm.usable_port_range)
  usable_ports.subtract(extra_in_use)

  # Pass one, remove all defined host ports from usable ports
  with_forwarded_ports(env) do |options|
    usable_ports.delete(options[:host])
  end

  # Pass two, detect/handle any collisions
  with_forwarded_ports(env) do |options|
    guest_port = options[:guest]
    host_port  = options[:host]

    if remap[host_port]
      remap_port = remap[host_port]
      @logger.debug("Remap port override: #{host_port} => #{remap_port}")
      host_port = remap_port
    end

    # If the port is open (listening for TCP connections)
    if extra_in_use.include?(host_port) || is_port_open?("127.0.0.1", host_port)
      if !repair
        raise Errors::ForwardPortCollision,
          :guest_port => guest_port.to_s,
          :host_port  => host_port.to_s
      end

      @logger.info("Attempting to repair FP collision: #{host_port}")

      # If we have no usable ports then we can't repair
      if usable_ports.empty?
        raise Errors::ForwardPortAutolistEmpty,
          :vm_name    => env[:machine].name,
          :guest_port => guest_port.to_s,
          :host_port  => host_port.to_s
      end

      # Attempt to repair the forwarded port
      repaired_port = usable_ports.to_a.sort[0]
      usable_ports.delete(repaired_port)

      # Modify the args in place
      options[:host] = repaired_port

      @logger.info("Repaired FP collision: #{host_port} to #{repaired_port}")

      # Notify the user
      env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision",
                           :host_port  => host_port.to_s,
                           :guest_port => guest_port.to_s,
                           :new_port   => repaired_port.to_s))
    end
  end

  @app.call(env)
end