Attach a driver to each VM. Use that to detect VirtualBox.
This commit is contained in:
parent
ee389c33f7
commit
9debf5abe9
|
@ -10,6 +10,7 @@ if ENV["VAGRANT_LOG"]
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'pathname'
|
require 'pathname'
|
||||||
|
require 'childprocess'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'i18n'
|
require 'i18n'
|
||||||
require 'virtualbox'
|
require 'virtualbox'
|
||||||
|
@ -27,6 +28,7 @@ module Vagrant
|
||||||
autoload :Config, 'vagrant/config'
|
autoload :Config, 'vagrant/config'
|
||||||
autoload :DataStore, 'vagrant/data_store'
|
autoload :DataStore, 'vagrant/data_store'
|
||||||
autoload :Downloaders, 'vagrant/downloaders'
|
autoload :Downloaders, 'vagrant/downloaders'
|
||||||
|
autoload :Driver, 'vagrant/driver'
|
||||||
autoload :Environment, 'vagrant/environment'
|
autoload :Environment, 'vagrant/environment'
|
||||||
autoload :Errors, 'vagrant/errors'
|
autoload :Errors, 'vagrant/errors'
|
||||||
autoload :Guest, 'vagrant/guest'
|
autoload :Guest, 'vagrant/guest'
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
module Vagrant
|
||||||
|
module Driver
|
||||||
|
autoload :VirtualBox, 'vagrant/driver/virtualbox'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,36 @@
|
||||||
|
require 'vagrant/util/subprocess'
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
module Driver
|
||||||
|
# This class contains the logic to drive VirtualBox.
|
||||||
|
class VirtualBox
|
||||||
|
# Include this so we can use `Subprocess` more easily.
|
||||||
|
include Vagrant::Util
|
||||||
|
|
||||||
|
# The version of virtualbox that is running.
|
||||||
|
attr_reader :version
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
# Read and assign the version of VirtualBox we know which
|
||||||
|
# specific driver to instantiate.
|
||||||
|
begin
|
||||||
|
@version = read_version
|
||||||
|
rescue Subprocess::ProcessFailedToStart
|
||||||
|
# This means that VirtualBox was not found, so we raise this
|
||||||
|
# error here.
|
||||||
|
raise Errors::VirtualBoxNotDetected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# This returns the version of VirtualBox that is running.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def read_version
|
||||||
|
result = Subprocess.execute("VBoxManage", "--version")
|
||||||
|
result.stdout.split("r")[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -36,27 +36,6 @@ module Vagrant
|
||||||
# The path to the default private key
|
# The path to the default private key
|
||||||
attr_reader :default_private_key_path
|
attr_reader :default_private_key_path
|
||||||
|
|
||||||
#---------------------------------------------------------------
|
|
||||||
# Class Methods
|
|
||||||
#---------------------------------------------------------------
|
|
||||||
class << self
|
|
||||||
# Verifies that VirtualBox is installed and that the version of
|
|
||||||
# VirtualBox installed is high enough.
|
|
||||||
def check_virtualbox!
|
|
||||||
version = VirtualBox.version
|
|
||||||
raise Errors::VirtualBoxNotDetected if version.nil?
|
|
||||||
raise Errors::VirtualBoxInvalidVersion, :version => version.to_s if version.to_f < 4.1 || version.to_f >= 4.2
|
|
||||||
rescue Errors::VirtualBoxNotDetected
|
|
||||||
# On 64-bit Windows, show a special error. This error is a subclass
|
|
||||||
# of VirtualBoxNotDetected, so libraries which use Vagrant can just
|
|
||||||
# rescue VirtualBoxNotDetected.
|
|
||||||
raise Errors::VirtualBoxNotDetected_Win64 if Util::Platform.windows? && Util::Platform.bit64?
|
|
||||||
|
|
||||||
# Otherwise, reraise the old error
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initializes a new environment with the given options. The options
|
# Initializes a new environment with the given options. The options
|
||||||
# is a hash where the main available key is `cwd`, which defines where
|
# is a hash where the main available key is `cwd`, which defines where
|
||||||
# the environment represents. There are other options available but
|
# the environment represents. There are other options available but
|
||||||
|
@ -331,10 +310,6 @@ module Vagrant
|
||||||
def load!
|
def load!
|
||||||
if !loaded?
|
if !loaded?
|
||||||
@loaded = true
|
@loaded = true
|
||||||
|
|
||||||
@logger.info("Environment not loaded. Checking virtual box version...")
|
|
||||||
self.class.check_virtualbox!
|
|
||||||
|
|
||||||
@logger.info("Loading configuration...")
|
@logger.info("Loading configuration...")
|
||||||
load_config!
|
load_config!
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
require 'childprocess'
|
||||||
|
require 'log4r'
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
module Util
|
||||||
|
# Execute a command in a subprocess, gathering the results and
|
||||||
|
# exit status.
|
||||||
|
#
|
||||||
|
# This class also allows you to read the data as it is outputted
|
||||||
|
# from the subprocess in real time, by simply passing a block to
|
||||||
|
# the execute method.
|
||||||
|
class Subprocess
|
||||||
|
# Convenience method for executing a method.
|
||||||
|
def self.execute(*command, &block)
|
||||||
|
new(*command).execute(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(*command)
|
||||||
|
@command = command
|
||||||
|
@logger = Log4r::Logger.new("vagrant::util::subprocess")
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
# Build the ChildProcess
|
||||||
|
@logger.debug("Starting process: #{@command.inspect}")
|
||||||
|
process = ChildProcess.build(*@command)
|
||||||
|
|
||||||
|
# Create the pipes so we can read the output in real time as
|
||||||
|
# we execute the command.
|
||||||
|
stdout, stdout_writer = IO.pipe
|
||||||
|
stderr, stderr_writer = IO.pipe
|
||||||
|
process.io.stdout = stdout_writer
|
||||||
|
process.io.stderr = stderr_writer
|
||||||
|
process.duplex = true
|
||||||
|
|
||||||
|
# Start the process
|
||||||
|
begin
|
||||||
|
process.start
|
||||||
|
rescue Exception => e
|
||||||
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
||||||
|
if e.is_a?(NativeException)
|
||||||
|
# This usually means that the process failed to start, so we
|
||||||
|
# raise that error.
|
||||||
|
raise ProcessFailedToStart
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure the stdin does not buffer
|
||||||
|
process.io.stdin.sync = true
|
||||||
|
|
||||||
|
# Close the writer pipes, since we're just reading
|
||||||
|
stdout_writer.close
|
||||||
|
stderr_writer.close
|
||||||
|
|
||||||
|
# Create a dictionary to store all the output we see.
|
||||||
|
io_data = { stdout => "", stderr => "" }
|
||||||
|
|
||||||
|
@logger.debug("Selecting on IO")
|
||||||
|
while true
|
||||||
|
results = IO.select([stdout, stderr], [process.io.stdin], nil, 5)
|
||||||
|
readers, writers = results
|
||||||
|
|
||||||
|
# Check the readers to see if they're ready
|
||||||
|
if !readers.empty?
|
||||||
|
begin
|
||||||
|
readers.each do |r|
|
||||||
|
data = r.read_nonblock(1024)
|
||||||
|
io_name = r == stdout ? :stdout : :stderr
|
||||||
|
@logger.debug(data)
|
||||||
|
|
||||||
|
if io_name == :stderr && io_data[r] == "" && data =~ /Errno::ENOENT/
|
||||||
|
# This is how we detect that a process failed to start on
|
||||||
|
# Linux. Hacky, but it works fairly well.
|
||||||
|
raise ProcessFailedToStart
|
||||||
|
end
|
||||||
|
|
||||||
|
io_data[r] += data
|
||||||
|
yield io_name, data if block_given?
|
||||||
|
end
|
||||||
|
rescue IO::WaitReadable
|
||||||
|
# This just means the IO wasn't actually ready and we
|
||||||
|
# should wait some more. No problem! Just pass on through...
|
||||||
|
rescue EOFError
|
||||||
|
# Process exited, most likely. We're done here.
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Break out if the process exited. We have to do this before
|
||||||
|
# attempting to write to stdin otherwise we'll get a broken pipe
|
||||||
|
# error.
|
||||||
|
break if process.exited?
|
||||||
|
|
||||||
|
# Check the writers to see if they're ready, and notify any listeners
|
||||||
|
if !writers.empty?
|
||||||
|
yield :stdin, process.io.stdin if block_given?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Wait for the process to end.
|
||||||
|
process.poll_for_exit(32000)
|
||||||
|
@logger.debug("Exit status: #{process.exit_code}")
|
||||||
|
|
||||||
|
# Return an exit status container
|
||||||
|
return Result.new(process.exit_code, io_data[stdout], io_data[stderr])
|
||||||
|
end
|
||||||
|
|
||||||
|
# An error which occurs when a process fails to start.
|
||||||
|
class ProcessFailedToStart < StandardError; end
|
||||||
|
|
||||||
|
# Container class to store the results of executing a subprocess.
|
||||||
|
class Result
|
||||||
|
attr_reader :exit_code
|
||||||
|
attr_reader :stdout
|
||||||
|
attr_reader :stderr
|
||||||
|
|
||||||
|
def initialize(exit_code, stdout, stderr)
|
||||||
|
@exit_code = exit_code
|
||||||
|
@stdout = stdout
|
||||||
|
@stderr = stderr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,7 @@ module Vagrant
|
||||||
attr_reader :vm
|
attr_reader :vm
|
||||||
attr_reader :box
|
attr_reader :box
|
||||||
attr_reader :config
|
attr_reader :config
|
||||||
|
attr_reader :driver
|
||||||
|
|
||||||
def initialize(name, env, config, vm=nil)
|
def initialize(name, env, config, vm=nil)
|
||||||
@logger = Log4r::Logger.new("vagrant::vm")
|
@logger = Log4r::Logger.new("vagrant::vm")
|
||||||
|
@ -18,6 +19,7 @@ module Vagrant
|
||||||
@env = env
|
@env = env
|
||||||
@config = config
|
@config = config
|
||||||
@box = env.boxes.find(config.vm.box)
|
@box = env.boxes.find(config.vm.box)
|
||||||
|
@driver = Driver::VirtualBox.new
|
||||||
|
|
||||||
# Load the associated guest.
|
# Load the associated guest.
|
||||||
load_guest!
|
load_guest!
|
||||||
|
|
|
@ -15,6 +15,7 @@ Gem::Specification.new do |s|
|
||||||
s.rubyforge_project = "vagrant"
|
s.rubyforge_project = "vagrant"
|
||||||
|
|
||||||
s.add_dependency "archive-tar-minitar", "= 0.5.2"
|
s.add_dependency "archive-tar-minitar", "= 0.5.2"
|
||||||
|
s.add_dependency "childprocess", "~> 0.2.3"
|
||||||
s.add_dependency "erubis", "~> 2.7.0"
|
s.add_dependency "erubis", "~> 2.7.0"
|
||||||
s.add_dependency "json", "~> 1.5.1"
|
s.add_dependency "json", "~> 1.5.1"
|
||||||
s.add_dependency "log4r", "~> 1.1.9"
|
s.add_dependency "log4r", "~> 1.1.9"
|
||||||
|
@ -27,8 +28,7 @@ Gem::Specification.new do |s|
|
||||||
s.add_development_dependency "contest", ">= 0.1.2"
|
s.add_development_dependency "contest", ">= 0.1.2"
|
||||||
s.add_development_dependency "minitest", "~> 2.5.1"
|
s.add_development_dependency "minitest", "~> 2.5.1"
|
||||||
s.add_development_dependency "mocha"
|
s.add_development_dependency "mocha"
|
||||||
s.add_development_dependency "childprocess", "~> 0.2.3"
|
#s.add_development_dependency "sys-proctable", "~> 0.9.0"
|
||||||
s.add_development_dependency "sys-proctable", "~> 0.9.0"
|
|
||||||
s.add_development_dependency "rspec-core", "~> 2.7.1"
|
s.add_development_dependency "rspec-core", "~> 2.7.1"
|
||||||
s.add_development_dependency "rspec-expectations", "~> 2.7.0"
|
s.add_development_dependency "rspec-expectations", "~> 2.7.0"
|
||||||
s.add_development_dependency "rspec-mocks", "~> 2.7.0"
|
s.add_development_dependency "rspec-mocks", "~> 2.7.0"
|
||||||
|
|
Loading…
Reference in New Issue