Merge pull request #39 from fgrehm/port-forwarding
Port forwarding support using redir
This commit is contained in:
commit
ad8d8cde81
8 changed files with 225 additions and 5 deletions
|
@ -5,12 +5,12 @@ Highly experimental Linux Containers support for Vagrant 1.1.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
Vagrant >= 1.1.0, the `lxc` package and a Kernel [higher than 3.5.0-17.28](#help-im-unable-to-restart-containers),
|
Vagrant >= 1.1.0, `lxc` and `redir` packages and a Kernel [higher than 3.5.0-17.28](#help-im-unable-to-restart-containers),
|
||||||
which on Ubuntu 12.10 means something like:
|
which on Ubuntu 12.10 means something like:
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo apt-get update && sudo apt-get dist-upgrade
|
sudo apt-get update && sudo apt-get dist-upgrade
|
||||||
sudo apt-get install lxc
|
sudo apt-get install lxc redir
|
||||||
wget "http://files.vagrantup.com/packages/67bd4d30f7dbefa7c0abc643599f0244986c38c8/vagrant_`uname -m`.deb" -O /tmp/vagrant.deb
|
wget "http://files.vagrantup.com/packages/67bd4d30f7dbefa7c0abc643599f0244986c38c8/vagrant_`uname -m`.deb" -O /tmp/vagrant.deb
|
||||||
sudo dpkg -i /tmp/vagrant.deb
|
sudo dpkg -i /tmp/vagrant.deb
|
||||||
```
|
```
|
||||||
|
@ -22,6 +22,7 @@ sudo dpkg -i /tmp/vagrant.deb
|
||||||
* Shared folders
|
* Shared folders
|
||||||
* Provisioning
|
* Provisioning
|
||||||
* Setting container's host name
|
* Setting container's host name
|
||||||
|
* Port forwarding
|
||||||
|
|
||||||
*Please refer to the [closed issues](https://github.com/fgrehm/vagrant-lxc/issues?labels=&milestone=&page=1&state=closed)
|
*Please refer to the [closed issues](https://github.com/fgrehm/vagrant-lxc/issues?labels=&milestone=&page=1&state=closed)
|
||||||
for the most up to date list.*
|
for the most up to date list.*
|
||||||
|
@ -29,7 +30,7 @@ for the most up to date list.*
|
||||||
|
|
||||||
## Current limitations
|
## Current limitations
|
||||||
|
|
||||||
* Port forwarding does not work [yet](https://github.com/fgrehm/vagrant-lxc/issues/6)
|
* Port forwarding collision detection
|
||||||
* A hell lot of `sudo`s
|
* A hell lot of `sudo`s
|
||||||
* Only a [single ubuntu box supported](boxes), I'm still [figuring out what should go
|
* Only a [single ubuntu box supported](boxes), I'm still [figuring out what should go
|
||||||
on the .box file](https://github.com/fgrehm/vagrant-lxc/issues/4)
|
on the .box file](https://github.com/fgrehm/vagrant-lxc/issues/4)
|
||||||
|
|
|
@ -20,7 +20,8 @@ exec {
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
package {
|
package {
|
||||||
[ 'libffi-dev', 'bsdtar', 'exuberant-ctags', 'ruby1.9.1-dev', 'htop', 'git', 'build-essential' ]:
|
[ 'libffi-dev', 'bsdtar', 'exuberant-ctags', 'ruby1.9.1-dev', 'htop', 'git',
|
||||||
|
'build-essential', 'redir' ]:
|
||||||
ensure => 'installed'
|
ensure => 'installed'
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,13 @@ require 'vagrant-lxc/action/base_action'
|
||||||
require 'vagrant-lxc/action/boot'
|
require 'vagrant-lxc/action/boot'
|
||||||
require 'vagrant-lxc/action/check_created'
|
require 'vagrant-lxc/action/check_created'
|
||||||
require 'vagrant-lxc/action/check_running'
|
require 'vagrant-lxc/action/check_running'
|
||||||
|
require 'vagrant-lxc/action/clear_forwarded_ports'
|
||||||
require 'vagrant-lxc/action/create'
|
require 'vagrant-lxc/action/create'
|
||||||
require 'vagrant-lxc/action/created'
|
require 'vagrant-lxc/action/created'
|
||||||
require 'vagrant-lxc/action/destroy'
|
require 'vagrant-lxc/action/destroy'
|
||||||
require 'vagrant-lxc/action/disconnect'
|
require 'vagrant-lxc/action/disconnect'
|
||||||
require 'vagrant-lxc/action/forced_halt'
|
require 'vagrant-lxc/action/forced_halt'
|
||||||
|
require 'vagrant-lxc/action/forward_ports'
|
||||||
require 'vagrant-lxc/action/handle_box_metadata'
|
require 'vagrant-lxc/action/handle_box_metadata'
|
||||||
require 'vagrant-lxc/action/is_running'
|
require 'vagrant-lxc/action/is_running'
|
||||||
require 'vagrant-lxc/action/network'
|
require 'vagrant-lxc/action/network'
|
||||||
|
@ -55,10 +57,10 @@ module Vagrant
|
||||||
# b.use ClearSharedFolders
|
# b.use ClearSharedFolders
|
||||||
b.use ShareFolders
|
b.use ShareFolders
|
||||||
b.use Network
|
b.use Network
|
||||||
# b.use ForwardPorts
|
|
||||||
b.use Vagrant::Action::Builtin::SetHostname
|
b.use Vagrant::Action::Builtin::SetHostname
|
||||||
# b.use SaneDefaults
|
# b.use SaneDefaults
|
||||||
# b.use Customize
|
# b.use Customize
|
||||||
|
b.use ForwardPorts
|
||||||
b.use Boot
|
b.use Boot
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -131,6 +133,7 @@ module Vagrant
|
||||||
if env[:result]
|
if env[:result]
|
||||||
# TODO: If vagrant >=...
|
# TODO: If vagrant >=...
|
||||||
b2.use Disconnect
|
b2.use Disconnect
|
||||||
|
b2.use ClearForwardedPorts
|
||||||
b2.use Vagrant::Action::Builtin::Call, Vagrant::Action::Builtin::GracefulHalt, :stopped, :running do |env2, b3|
|
b2.use Vagrant::Action::Builtin::Call, Vagrant::Action::Builtin::GracefulHalt, :stopped, :running do |env2, b3|
|
||||||
if !env2[:result] && env2[:machine].provider.state.running?
|
if !env2[:result] && env2[:machine].provider.state.running?
|
||||||
b3.use ForcedHalt
|
b3.use ForcedHalt
|
||||||
|
|
47
lib/vagrant-lxc/action/clear_forwarded_ports.rb
Normal file
47
lib/vagrant-lxc/action/clear_forwarded_ports.rb
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
module Vagrant
|
||||||
|
module LXC
|
||||||
|
module Action
|
||||||
|
class ClearForwardedPorts
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@logger = Log4r::Logger.new("vagrant::lxc::action::clear_forwarded_ports")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
@env = env
|
||||||
|
|
||||||
|
if redir_pids.any?
|
||||||
|
env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting")
|
||||||
|
redir_pids.each do |pid|
|
||||||
|
next unless is_redir_pid?(pid)
|
||||||
|
@logger.debug "Killing pid #{pid}"
|
||||||
|
system "sudo pkill -9 -P #{pid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_redir_pids
|
||||||
|
end
|
||||||
|
|
||||||
|
@app.call env
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def redir_pids
|
||||||
|
@redir_pids = Dir[@env[:machine].data_dir.join('pids').to_s + "/redir_*.pid"].map do |file|
|
||||||
|
File.read(file).strip.chomp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_redir_pid?(pid)
|
||||||
|
`ps -o cmd= #{pid}`.strip.chomp =~ /redir/
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_redir_pids
|
||||||
|
Dir[@env[:machine].data_dir.join('pids').to_s + "/redir_*.pid"].each do |file|
|
||||||
|
File.delete file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
83
lib/vagrant-lxc/action/forward_ports.rb
Normal file
83
lib/vagrant-lxc/action/forward_ports.rb
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
module Vagrant
|
||||||
|
module LXC
|
||||||
|
module Action
|
||||||
|
class ForwardPorts
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@logger = Log4r::Logger.new("vagrant::lxc::action::forward_ports")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
@env = env
|
||||||
|
|
||||||
|
# Continue, we need the VM to be booted in order to grab its IP
|
||||||
|
@app.call env
|
||||||
|
|
||||||
|
# Get the ports we're forwarding
|
||||||
|
env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)
|
||||||
|
|
||||||
|
# Warn if we're port forwarding to any privileged ports
|
||||||
|
env[:forwarded_ports].each do |fp|
|
||||||
|
if fp[:host] <= 1024
|
||||||
|
env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
|
||||||
|
forward_ports
|
||||||
|
end
|
||||||
|
|
||||||
|
def forward_ports
|
||||||
|
@container_ip = @env[:machine].provider.container.assigned_ip
|
||||||
|
|
||||||
|
@env[:forwarded_ports].each do |fp|
|
||||||
|
message_attributes = {
|
||||||
|
# TODO: Add support for multiple adapters
|
||||||
|
:adapter => 'eth0',
|
||||||
|
:guest_port => fp[:guest],
|
||||||
|
:host_port => fp[:host]
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: Remove adapter from logging
|
||||||
|
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
|
||||||
|
message_attributes))
|
||||||
|
|
||||||
|
redir_pid = redirect_port(fp[:host], fp[:guest])
|
||||||
|
store_redir_pid(fp[:host], redir_pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def compile_forwarded_ports(config)
|
||||||
|
mappings = {}
|
||||||
|
|
||||||
|
config.vm.networks.each do |type, options|
|
||||||
|
if type == :forwarded_port
|
||||||
|
mappings[options[:host]] = options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mappings.values
|
||||||
|
end
|
||||||
|
|
||||||
|
def redirect_port(host, guest)
|
||||||
|
redir_cmd = "sudo redir --laddr=127.0.0.1 --lport=#{host} --cport=#{guest} --caddr=#{@container_ip}"
|
||||||
|
|
||||||
|
@logger.debug "Forwarding port with `#{redir_cmd}`"
|
||||||
|
fork { exec redir_cmd }
|
||||||
|
end
|
||||||
|
|
||||||
|
def store_redir_pid(host_port, redir_pid)
|
||||||
|
data_dir = @env[:machine].data_dir.join('pids')
|
||||||
|
data_dir.mkdir unless data_dir.directory?
|
||||||
|
|
||||||
|
data_dir.join("redir_#{host_port}.pid").open('w') do |pid_file|
|
||||||
|
pid_file.write(redir_pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,8 @@ end
|
||||||
|
|
||||||
require 'bundler/setup'
|
require 'bundler/setup'
|
||||||
|
|
||||||
|
require 'i18n'
|
||||||
|
|
||||||
require 'rspec-spies'
|
require 'rspec-spies'
|
||||||
|
|
||||||
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }
|
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }
|
||||||
|
|
42
spec/unit/action/clear_forwarded_ports_spec.rb
Normal file
42
spec/unit/action/clear_forwarded_ports_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
require 'unit_helper'
|
||||||
|
|
||||||
|
require 'vagrant-lxc/action/clear_forwarded_ports'
|
||||||
|
|
||||||
|
describe Vagrant::LXC::Action::ClearForwardedPorts do
|
||||||
|
let(:app) { mock(:app, call: true) }
|
||||||
|
let(:env) { {machine: machine, ui: stub(info: true)} }
|
||||||
|
let(:machine) { mock(:machine, data_dir: data_dir) }
|
||||||
|
let!(:data_dir) { Pathname.new(Dir.mktmpdir) }
|
||||||
|
let(:pids_dir) { data_dir.join('pids') }
|
||||||
|
let(:pid) { 'a-pid' }
|
||||||
|
let(:pid_cmd) { 'redir' }
|
||||||
|
|
||||||
|
subject { described_class.new(app, env) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
pids_dir.mkdir
|
||||||
|
pids_dir.join('redir_1234.pid').open('w') { |f| f.write(pid) }
|
||||||
|
subject.stub(system: true, :` => pid_cmd)
|
||||||
|
subject.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
after { FileUtils.rm_rf data_dir.to_s }
|
||||||
|
|
||||||
|
it 'removes all files under pid directory' do
|
||||||
|
Dir[pids_dir.to_s + "/redir_*.pid"].should be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a valid redir pid' do
|
||||||
|
it 'kills known processes' do
|
||||||
|
subject.should have_received(:system).with("sudo pkill -9 -P #{pid}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an invalid pid' do
|
||||||
|
let(:pid_cmd) { 'sudo ls' }
|
||||||
|
|
||||||
|
it 'does not kill the process' do
|
||||||
|
subject.should_not have_received(:system).with("sudo pkill -9 -P #{pid}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
spec/unit/action/forward_ports_spec.rb
Normal file
41
spec/unit/action/forward_ports_spec.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
require 'unit_helper'
|
||||||
|
|
||||||
|
require 'vagrant-lxc/action/forward_ports'
|
||||||
|
|
||||||
|
describe Vagrant::LXC::Action::ForwardPorts do
|
||||||
|
let(:app) { mock(:app, call: true) }
|
||||||
|
let(:env) { {machine: machine, ui: stub(info: true)} }
|
||||||
|
let(:machine) { mock(:machine) }
|
||||||
|
let!(:data_dir) { Pathname.new(Dir.mktmpdir) }
|
||||||
|
let(:networks) { [[:other_config, {}], [:forwarded_port, {guest: guest_port, host: host_port}]] }
|
||||||
|
let(:host_port) { 8080 }
|
||||||
|
let(:guest_port) { 80 }
|
||||||
|
let(:provider) { fire_double('Vagrant::LXC::Provider', container: container) }
|
||||||
|
let(:container) { fire_double('Vagrant::LXC::Container', assigned_ip: container_ip) }
|
||||||
|
let(:container_ip) { '10.0.1.234' }
|
||||||
|
let(:pid) { 'a-pid' }
|
||||||
|
|
||||||
|
subject { described_class.new(app, env) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
machine.stub_chain(:config, :vm, :networks).and_return(networks)
|
||||||
|
machine.stub(provider: provider, data_dir: data_dir)
|
||||||
|
|
||||||
|
subject.stub(exec: true)
|
||||||
|
subject.stub(:fork) { |&block| block.call; pid }
|
||||||
|
subject.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
after { FileUtils.rm_rf data_dir.to_s }
|
||||||
|
|
||||||
|
it 'forwards ports using redir' do
|
||||||
|
subject.should have_received(:exec).with(
|
||||||
|
"sudo redir --laddr=127.0.0.1 --lport=#{host_port} --cport=#{guest_port} --caddr=#{container_ip}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stores redir pids on machine's data dir" do
|
||||||
|
pid_file = data_dir.join('pids', "redir_#{host_port}.pid").read
|
||||||
|
pid_file.should == pid
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue