340 lines
10 KiB
Ruby
340 lines
10 KiB
Ruby
module Vagrant
|
|
# Represents a single Vagrant environment. This class is responsible
|
|
# for loading all of the Vagrantfile's for the given environment and
|
|
# storing references to the various instances.
|
|
class Environment
|
|
ROOTFILE_NAME = "Vagrantfile"
|
|
HOME_SUBDIRS = ["tmp", "boxes"]
|
|
DEFAULT_VM = :default
|
|
|
|
include Util
|
|
|
|
attr_reader :parent # Parent environment (in the case of multi-VMs)
|
|
attr_reader :vm_name # The name of the VM (internal name) which this environment represents
|
|
|
|
attr_accessor :cwd
|
|
attr_reader :root_path
|
|
attr_reader :config
|
|
attr_reader :box
|
|
attr_accessor :vm
|
|
attr_reader :vms
|
|
attr_reader :active_list
|
|
attr_reader :commands
|
|
attr_reader :logger
|
|
|
|
#---------------------------------------------------------------
|
|
# Class Methods
|
|
#---------------------------------------------------------------
|
|
class << self
|
|
# Loads and returns an environment given a specific working
|
|
# directory. If a working directory is not given, it will default
|
|
# to the pwd.
|
|
def load!(cwd=nil)
|
|
Environment.new(:cwd => cwd).load!
|
|
end
|
|
|
|
# Verifies that VirtualBox is installed and that the version of
|
|
# VirtualBox installed is high enough. Also verifies that the
|
|
# configuration path is properly set.
|
|
def check_virtualbox!
|
|
version = VirtualBox.version
|
|
if version.nil?
|
|
error_and_exit(:virtualbox_not_detected)
|
|
elsif version.to_f < 3.1
|
|
error_and_exit(:virtualbox_invalid_version, :version => version.to_s)
|
|
elsif version.to_s.downcase.include?("ose")
|
|
error_and_exit(:virtualbox_invalid_ose, :version => version.to_s)
|
|
end
|
|
end
|
|
end
|
|
|
|
def initialize(opts=nil)
|
|
defaults = {
|
|
:parent => nil,
|
|
:vm_name => nil,
|
|
:vm => nil,
|
|
:cwd => nil
|
|
}
|
|
|
|
opts = defaults.merge(opts || {})
|
|
|
|
defaults.each do |key, value|
|
|
instance_variable_set("@#{key}".to_sym, opts[key])
|
|
end
|
|
end
|
|
|
|
#---------------------------------------------------------------
|
|
# Helpers
|
|
#---------------------------------------------------------------
|
|
|
|
# Specifies the "current working directory" for this environment.
|
|
# This is vital in determining the root path and therefore the
|
|
# dotfile, rootpath vagrantfile, etc. This defaults to the
|
|
# actual cwd (`Dir.pwd`).
|
|
def cwd
|
|
@cwd || Dir.pwd
|
|
end
|
|
|
|
# The path to the `dotfile`, which contains the persisted UUID of
|
|
# the VM if it exists.
|
|
def dotfile_path
|
|
root_path ? File.join(root_path, config.vagrant.dotfile_name) : nil
|
|
end
|
|
|
|
# The path to the home directory, which is usually in `~/.vagrant/~
|
|
def home_path
|
|
config ? config.vagrant.home : nil
|
|
end
|
|
|
|
# The path to the Vagrant tmp directory
|
|
def tmp_path
|
|
File.join(home_path, "tmp")
|
|
end
|
|
|
|
# The path to the Vagrant boxes directory
|
|
def boxes_path
|
|
File.join(home_path, "boxes")
|
|
end
|
|
|
|
# Returns the VMs associated with this environment.
|
|
def vms
|
|
@vms ||= {}
|
|
end
|
|
|
|
# Returns a boolean whether this environment represents a multi-VM
|
|
# environment or not. This will work even when called on child
|
|
# environments.
|
|
def multivm?
|
|
if parent
|
|
parent.multivm?
|
|
else
|
|
vms.length > 1
|
|
end
|
|
end
|
|
|
|
#---------------------------------------------------------------
|
|
# Load Methods
|
|
#---------------------------------------------------------------
|
|
|
|
# Loads this entire environment, setting up the instance variables
|
|
# such as `vm`, `config`, etc. on this environment. The order this
|
|
# method calls its other methods is very particular.
|
|
def load!
|
|
load_logger!
|
|
load_root_path!
|
|
load_config!
|
|
load_home_directory!
|
|
load_box!
|
|
load_config!
|
|
self.class.check_virtualbox!
|
|
load_vm!
|
|
load_active_list!
|
|
load_commands!
|
|
self
|
|
end
|
|
|
|
# Loads the root path of this environment, given the starting
|
|
# directory (the "cwd" of this environment for lack of better words).
|
|
# This method allows an environment in `/foo` to be detected from
|
|
# `/foo/bar` (similar to how git works in subdirectories)
|
|
def load_root_path!(path=nil)
|
|
path = Pathname.new(File.expand_path(path || cwd))
|
|
|
|
# Stop if we're at the root.
|
|
return false if path.root?
|
|
|
|
file = "#{path}/#{ROOTFILE_NAME}"
|
|
if File.exist?(file)
|
|
@root_path = path.to_s
|
|
return true
|
|
end
|
|
|
|
load_root_path!(path.parent)
|
|
end
|
|
|
|
# Loads this environment's configuration and stores it in the {config}
|
|
# variable. The configuration loaded by this method is specified to
|
|
# this environment, meaning that it will use the given root directory
|
|
# to load the Vagrantfile into that context.
|
|
def load_config!
|
|
# Prepare load paths for config files and append to config queue
|
|
config_queue = [File.join(PROJECT_ROOT, "config", "default.rb")]
|
|
config_queue << File.join(box.directory, ROOTFILE_NAME) if box
|
|
config_queue << File.join(home_path, ROOTFILE_NAME) if home_path
|
|
config_queue << File.join(root_path, ROOTFILE_NAME) if root_path
|
|
|
|
# If this environment represents some VM in a multi-VM environment,
|
|
# we push that VM's configuration onto the config_queue.
|
|
config_queue << parent.config.vm.defined_vms[vm_name] if vm_name
|
|
|
|
# Clear out the old data
|
|
Config.reset!(self)
|
|
|
|
# Load each of the config files in order
|
|
config_queue.each do |item|
|
|
if item.is_a?(String) && File.exist?(item)
|
|
load item
|
|
next
|
|
end
|
|
|
|
if item.is_a?(Proc)
|
|
# Just push the proc straight onto the config runnable stack
|
|
Config.run(&item)
|
|
end
|
|
end
|
|
|
|
# Execute the configuration stack and store the result
|
|
@config = Config.execute!
|
|
|
|
# (re)load the logger
|
|
load_logger!
|
|
end
|
|
|
|
# Loads the logger for this environment. This is called by
|
|
# {#load_config!} so that the logger is only loaded after
|
|
# configuration information is available. The logger is also
|
|
# loaded early on in the load chain process so that the various
|
|
# references to logger won't throw nil exceptions, but by default
|
|
# the logger will just send the log data to a black hole.
|
|
def load_logger!
|
|
resource = vm_name || "vagrant"
|
|
@logger = ResourceLogger.new(resource, self)
|
|
end
|
|
|
|
# Loads the home directory path and creates the necessary subdirectories
|
|
# within the home directory if they're not already created.
|
|
def load_home_directory!
|
|
# Setup the array of necessary home directories
|
|
dirs = HOME_SUBDIRS.collect { |subdir| File.join(home_path, subdir) }
|
|
dirs.unshift(home_path)
|
|
|
|
# Go through each required directory, creating it if it doesn't exist
|
|
dirs.each do |dir|
|
|
next if File.directory?(dir)
|
|
|
|
logger.info "Creating home directory since it doesn't exist: #{dir}"
|
|
FileUtils.mkdir_p(dir)
|
|
end
|
|
end
|
|
|
|
# Loads the specified box for this environment.
|
|
def load_box!
|
|
return unless root_path
|
|
|
|
@box = Box.find(self, config.vm.box) if config.vm.box
|
|
end
|
|
|
|
# Loads the persisted VM (if it exists) for this environment.
|
|
def load_vm!
|
|
# This environment represents a single sub VM. The VM is then
|
|
# probably (read: should be) set on the VM attribute, so we do
|
|
# nothing.
|
|
return if vm_name
|
|
|
|
# First load the defaults (blank, noncreated VMs)
|
|
load_blank_vms!
|
|
|
|
# If we have no dotfile, then return
|
|
return if !dotfile_path || !File.file?(dotfile_path)
|
|
|
|
# Open and parse the dotfile
|
|
File.open(dotfile_path) do |f|
|
|
data = { DEFAULT_VM => f.read }
|
|
|
|
begin
|
|
data = JSON.parse(data[DEFAULT_VM])
|
|
rescue JSON::ParserError
|
|
# Most likely an older (<= 0.3.x) dotfile. Try to load it
|
|
# as the :__vagrant VM.
|
|
end
|
|
|
|
data.each do |key, value|
|
|
key = key.to_sym
|
|
vms[key] = Vagrant::VM.find(value, self, key)
|
|
end
|
|
end
|
|
rescue Errno::ENOENT
|
|
# Just rescue it.
|
|
end
|
|
|
|
# Loads blank VMs into the `vms` attribute.
|
|
def load_blank_vms!
|
|
# Clear existing vms
|
|
vms.clear
|
|
|
|
# Load up the blank VMs
|
|
defined_vms = config.vm.defined_vms.keys
|
|
defined_vms = [DEFAULT_VM] if defined_vms.empty?
|
|
|
|
defined_vms.each do |name|
|
|
vms[name] = Vagrant::VM.new(:vm_name => name, :env => self)
|
|
end
|
|
end
|
|
|
|
# Loads the activelist for this environment
|
|
def load_active_list!
|
|
@active_list = ActiveList.new(self)
|
|
end
|
|
|
|
# Loads the instance of {Command} for this environment. This allows
|
|
# users of the instance to run commands such as "up" "down" etc. in
|
|
# the context of this environment.
|
|
def load_commands!
|
|
@commands = Command.new(self)
|
|
end
|
|
|
|
#---------------------------------------------------------------
|
|
# Methods to manage VM
|
|
#---------------------------------------------------------------
|
|
|
|
# Persists this environment's VM to the dotfile so it can be
|
|
# re-loaded at a later time.
|
|
def update_dotfile
|
|
if parent
|
|
parent.update_dotfile
|
|
return
|
|
end
|
|
|
|
# Generate and save the persisted VM info
|
|
data = vms.inject({}) do |acc, data|
|
|
key, value = data
|
|
acc[key] = value.uuid if value.created?
|
|
acc
|
|
end
|
|
|
|
File.open(dotfile_path, 'w+') do |f|
|
|
f.write(data.to_json)
|
|
end
|
|
|
|
# Also add to the global store
|
|
# active_list.add(vm)
|
|
end
|
|
|
|
#---------------------------------------------------------------
|
|
# Methods to check for properties and error
|
|
#---------------------------------------------------------------
|
|
|
|
def require_root_path
|
|
error_and_exit(:rootfile_not_found) if !root_path
|
|
end
|
|
|
|
def require_box
|
|
require_root_path
|
|
|
|
if !box
|
|
if !config.vm.box
|
|
error_and_exit(:box_not_specified)
|
|
else
|
|
error_and_exit(:box_specified_doesnt_exist, :box_name => config.vm.box)
|
|
end
|
|
end
|
|
end
|
|
|
|
def require_persisted_vm
|
|
require_root_path
|
|
|
|
error_and_exit(:environment_not_created) if !vm
|
|
end
|
|
end
|
|
end
|