vagrant/test/acceptance/support/isolated_environment.rb

118 lines
4.0 KiB
Ruby

require "fileutils"
require "pathname"
require "log4r"
require "childprocess"
require "vagrant/util/subprocess"
require "acceptance/support/virtualbox"
require "support/isolated_environment"
module Acceptance
# This class manages an isolated environment for Vagrant to
# run in. It creates a temporary directory to act as the
# working directory as well as sets a custom home directory.
class IsolatedEnvironment < ::IsolatedEnvironment
SKELETON_DIR = Pathname.new(File.expand_path("../../skeletons", __FILE__))
def initialize(apps=nil, env=nil)
super()
@logger = Log4r::Logger.new("test::acceptance::isolated_environment")
@apps = apps.clone || {}
@env = env.clone || {}
# Set the home directory and virtualbox home directory environmental
# variables so that Vagrant and VirtualBox see the proper paths here.
@env["HOME"] ||= @homedir.to_s
@env["VBOX_USER_HOME"] ||= @homedir.to_s
end
# Copies a skeleton into this isolated environment. This is useful
# for testing environments that require a complex setup.
#
# @param [String] name Name of the skeleton in the skeletons/ directory.
def skeleton!(name)
# Copy all the files into the home directory
source = Dir.glob(SKELETON_DIR.join(name).join("*").to_s)
FileUtils.cp_r(source, @workdir.to_s)
end
# Executes a command in the context of this isolated environment.
# Any command executed will therefore see our temporary directory
# as the home directory.
def execute(command, *argN)
# Create the command
command = replace_command(command)
# Determine the options
options = argN.last.is_a?(Hash) ? argN.pop : {}
options = {
:workdir => @workdir,
:env => @env
}.merge(options)
# Add the options to be passed on
argN << options
# Execute, logging out the stdout/stderr as we get it
@logger.info("Executing: #{[command].concat(argN).inspect}")
Vagrant::Util::Subprocess.execute(command, *argN) do |type, data|
@logger.debug("#{type}: #{data}") if type == :stdout || type == :stderr
yield type, data if block_given?
end
end
# Closes the environment, cleans up the temporary directories, etc.
def close
# Only delete virtual machines if VBoxSVC is running, meaning
# that something related to VirtualBox started running in this
# environment.
delete_virtual_machines if VirtualBox.find_vboxsvc
# Let the parent handle cleaning up
super
end
def delete_virtual_machines
# Delete all virtual machines
@logger.debug("Finding all virtual machines")
execute("VBoxManage", "list", "vms").stdout.lines.each do |line|
data = /^"(?<name>.+?)" {(?<uuid>.+?)}$/.match(line)
begin
@logger.debug("Removing VM: #{data[:name]}")
# We add a timeout onto this because sometimes for seemingly no
# reason it will simply freeze, although the VM is successfully
# "aborted." The timeout gets around this strange behavior.
execute("VBoxManage", "controlvm", data[:uuid], "poweroff", :timeout => 5)
rescue Vagrant::Util::Subprocess::TimeoutExceeded => e
@logger.info("Failed to poweroff VM '#{data[:uuid]}'. Killing process.")
# Kill the process and wait a bit for it to disappear
Process.kill('KILL', e.pid)
Process.waitpid2(e.pid)
end
sleep 0.5
result = execute("VBoxManage", "unregistervm", data[:uuid], "--delete")
raise Exception, "VM unregistration failed!" if result.exit_code != 0
end
@logger.info("Removed all virtual machines")
end
# This replaces a command with a replacement defined when this
# isolated environment was initialized. If nothing was defined,
# then the command itself is returned.
def replace_command(command)
return @apps[command] if @apps.has_key?(command)
return command
end
end
end