Replace ugly driver builder with a more intelligent approach

References #118
This commit is contained in:
Fabio Rehm 2013-07-29 12:08:22 -03:00
parent b374bba4ec
commit 0385a64d31
9 changed files with 96 additions and 69 deletions

View file

@ -8,6 +8,8 @@ require 'vagrant-lxc/action/destroy'
require 'vagrant-lxc/action/destroy_confirm' require 'vagrant-lxc/action/destroy_confirm'
require 'vagrant-lxc/action/disconnect' require 'vagrant-lxc/action/disconnect'
require 'vagrant-lxc/action/compress_rootfs' require 'vagrant-lxc/action/compress_rootfs'
require 'vagrant-lxc/action/fetch_ip_with_lxc_attach'
require 'vagrant-lxc/action/fetch_ip_from_dnsmasq_leases'
require 'vagrant-lxc/action/forced_halt' require 'vagrant-lxc/action/forced_halt'
require 'vagrant-lxc/action/forward_ports' require 'vagrant-lxc/action/forward_ports'
require 'vagrant-lxc/action/handle_box_metadata' require 'vagrant-lxc/action/handle_box_metadata'
@ -169,6 +171,16 @@ module Vagrant
end end
end end
# This action is called to read the IP of the container. The IP found
# is expected to be put into the `:machine_ip` key.
def self.action_fetch_ip
Vagrant::Action::Builder.new.tap do |b|
b.use Vagrant::Action::Builtin::ConfigValidate
b.use FetchIpWithLxcAttach
b.use FetchIpFromDnsmasqLeases
end
end
# This is the action that will exec into an SSH shell. # This is the action that will exec into an SSH shell.
def self.action_ssh def self.action_ssh
Vagrant::Action::Builder.new.tap do |b| Vagrant::Action::Builder.new.tap do |b|

View file

@ -1,11 +1,21 @@
module Vagrant module Vagrant
module LXC module LXC
class Driver module Action
module FetchIpFromDsnmasq class FetchIpFromDnsmasqLeases
def assigned_ip def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_from_dnsmasq_leases")
end
def call(env)
env[:machine_ip] ||= assigned_ip(env)
@app.call(env)
end
def assigned_ip(env)
@logger.debug 'Loading ip from dnsmasq leases' @logger.debug 'Loading ip from dnsmasq leases'
mac_address = env[:machine].provider.driver.mac_address
ip = nil ip = nil
# TODO: Use Vagrant::Util::Retryable
10.times do 10.times do
if dnsmasq_leases =~ /#{Regexp.escape mac_address}\s+([0-9.]+)\s+/ if dnsmasq_leases =~ /#{Regexp.escape mac_address}\s+([0-9.]+)\s+/
ip = $1.to_s ip = $1.to_s
@ -15,15 +25,9 @@ module Vagrant
sleep 2 sleep 2
end end
end end
# TODO: Raise an user friendly error
raise 'Unable to identify container IP!' unless ip
ip ip
end end
def mac_address
@mac_address ||= base_path.join('config').read.match(/^lxc\.network\.hwaddr\s+=\s+(.+)$/)[1]
end
LEASES_PATHS = %w( LEASES_PATHS = %w(
/var/lib/misc/dnsmasq.leases /var/lib/misc/dnsmasq.leases
/var/lib/dnsmasq/dnsmasq.leases /var/lib/dnsmasq/dnsmasq.leases

View file

@ -0,0 +1,46 @@
module Vagrant
module LXC
module Action
class FetchIpWithLxcAttach
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_with_lxc_attach")
end
def call(env)
env[:machine_ip] ||= assigned_ip(env)
@app.call(env)
end
def assigned_ip(env)
driver = env[:machine].provider.driver
version = driver.version.match(/^(\d+\.\d+)\./)[1].to_f
unless version >= 0.8
@logger.debug "lxc version does not support the --namespaces argument to lxc-attach"
return nil
end
ip = ''
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
unless ip = get_container_ip_from_ip_addr(driver)
# retry
raise LXC::Errors::ExecuteError, :command => "lxc-attach"
end
end
ip
end
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
def get_container_ip_from_ip_addr(driver)
output = driver.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: 'network'
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
return $1.to_s
end
end
end
end
end
end

View file

@ -50,7 +50,7 @@ module Vagrant
redir_pid = redirect_port( redir_pid = redirect_port(
fp[:host_ip], fp[:host_ip],
fp[:host], fp[:host],
fp[:guest_ip] || @env[:machine].provider.driver.assigned_ip, fp[:guest_ip] || @env[:machine].provider.ssh_info[:host],
fp[:guest] fp[:guest]
) )
store_redir_pid(fp[:host], redir_pid) store_redir_pid(fp[:host], redir_pid)

View file

@ -34,6 +34,10 @@ module Vagrant
Pathname.new(base_path.join('config').read.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1]) Pathname.new(base_path.join('config').read.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1])
end end
def mac_address
@mac_address ||= base_path.join('config').read.match(/^lxc\.network\.hwaddr\s+=\s+(.+)$/)[1]
end
def create(name, template_path, config_file, template_options = {}) def create(name, template_path, config_file, template_options = {})
@cli.name = @container_name = name @cli.name = @container_name = name
@ -81,6 +85,14 @@ module Vagrant
@cli.destroy @cli.destroy
end end
def attach(*command)
@cli.attach(*command)
end
def version
@cli.version
end
# TODO: This needs to be reviewed and specs needs to be written # TODO: This needs to be reviewed and specs needs to be written
def compress_rootfs def compress_rootfs
rootfs_dirname = File.dirname rootfs_path rootfs_dirname = File.dirname rootfs_path
@ -106,9 +118,6 @@ module Vagrant
end end
end end
def assigned_ip
end
protected protected
# Root folder where container configs are stored # Root folder where container configs are stored

View file

@ -1,21 +0,0 @@
require_relative 'fetch_ip_with_attach'
require_relative 'fetch_ip_from_dnsmasq'
module Vagrant
module LXC
class Driver
class Builder
def self.build(id, shell)
version = CLI.new(shell).version.match(/^(\d+\.\d+)\./)[1].to_f
Driver.new(id, shell).tap do |driver|
mod = version >= 0.8 ?
Driver::FetchIpWithAttach :
Driver::FetchIpFromDsnmasq
driver.extend(mod)
end
end
end
end
end
end

View file

@ -1,29 +0,0 @@
module Vagrant
module LXC
class Driver
module FetchIpWithAttach
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
def assigned_ip
ip = ''
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
unless ip = get_container_ip_from_ip_addr
# retry
raise LXC::Errors::ExecuteError, :command => "lxc-attach"
end
end
ip
end
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
def get_container_ip_from_ip_addr
output = @cli.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: 'network'
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
return $1.to_s
end
end
end
end
end
end

View file

@ -2,7 +2,6 @@ require "log4r"
require "vagrant-lxc/action" require "vagrant-lxc/action"
require "vagrant-lxc/driver" require "vagrant-lxc/driver"
require "vagrant-lxc/driver/builder"
require "vagrant-lxc/sudo_wrapper" require "vagrant-lxc/sudo_wrapper"
module Vagrant module Vagrant
@ -39,7 +38,7 @@ module Vagrant
begin begin
@logger.debug("Instantiating the container for: #{id.inspect}") @logger.debug("Instantiating the container for: #{id.inspect}")
@driver = Driver::Builder.build(id, self.sudo_wrapper) @driver = Driver.new(id, self.sudo_wrapper)
@driver.validate! @driver.validate!
rescue Driver::ContainerNotFound rescue Driver::ContainerNotFound
# The container doesn't exist, so we probably have a stale # The container doesn't exist, so we probably have a stale
@ -66,8 +65,16 @@ module Vagrant
# we return nil. # we return nil.
return nil if state == :not_created return nil if state == :not_created
# Run a custom action called "fetch_ip" which does what it says and puts
# the IP found into the `:machine_ip` key in the environment.
env = @machine.action("fetch_ip")
# If we were not able to identify the container's IP, we return nil
# here and we let Vagrant core deal with it ;)
return nil unless env[:machine_ip]
{ {
:host => @driver.assigned_ip, :host => env[:machine_ip],
:port => @machine.config.ssh.guest_port :port => @machine.config.ssh.guest_port
} }
end end

View file

@ -9,8 +9,7 @@ describe Vagrant::LXC::Action::ForwardPorts do
let(:env) { {machine: machine, ui: double(info: true)} } let(:env) { {machine: machine, ui: double(info: true)} }
let(:machine) { double(:machine) } let(:machine) { double(:machine) }
let!(:data_dir) { Pathname.new(Dir.mktmpdir) } let!(:data_dir) { Pathname.new(Dir.mktmpdir) }
let(:provider) { instance_double('Vagrant::LXC::Provider', driver: driver) } let(:provider) { instance_double('Vagrant::LXC::Provider', ssh_info: {host: container_ip}) }
let(:driver) { instance_double('Vagrant::LXC::Driver', assigned_ip: container_ip) }
let(:host_ip) { '127.0.0.1' } let(:host_ip) { '127.0.0.1' }
let(:host_port) { 8080 } let(:host_port) { 8080 }
let(:guest_port) { 80 } let(:guest_port) { 80 }