Improve the thread safety of BoxCollection
This commit is contained in:
parent
9dd582be3a
commit
04d9872674
|
@ -1,4 +1,5 @@
|
||||||
require "digest/sha1"
|
require "digest/sha1"
|
||||||
|
require "thread"
|
||||||
require "tmpdir"
|
require "tmpdir"
|
||||||
|
|
||||||
require "log4r"
|
require "log4r"
|
||||||
|
@ -43,6 +44,7 @@ module Vagrant
|
||||||
options ||= {}
|
options ||= {}
|
||||||
|
|
||||||
@directory = directory
|
@directory = directory
|
||||||
|
@lock = Mutex.new
|
||||||
@temp_root = options[:temp_dir_root]
|
@temp_root = options[:temp_dir_root]
|
||||||
@logger = Log4r::Logger.new("vagrant::box_collection")
|
@logger = Log4r::Logger.new("vagrant::box_collection")
|
||||||
end
|
end
|
||||||
|
@ -69,6 +71,7 @@ module Vagrant
|
||||||
# @param [Boolean] force If true, any existing box with the same name
|
# @param [Boolean] force If true, any existing box with the same name
|
||||||
# and provider will be replaced.
|
# and provider will be replaced.
|
||||||
def add(path, name, provider=nil, force=false)
|
def add(path, name, provider=nil, force=false)
|
||||||
|
with_collection_lock do
|
||||||
# A helper to check if a box exists. We store this in a variable
|
# A helper to check if a box exists. We store this in a variable
|
||||||
# since we call it multiple times.
|
# since we call it multiple times.
|
||||||
check_box_exists = lambda do |box_provider|
|
check_box_exists = lambda do |box_provider|
|
||||||
|
@ -163,6 +166,7 @@ module Vagrant
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Return the box
|
# Return the box
|
||||||
find(name, provider)
|
find(name, provider)
|
||||||
|
@ -177,6 +181,7 @@ module Vagrant
|
||||||
def all
|
def all
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
with_collection_lock do
|
||||||
@logger.debug("Finding all boxes in: #{@directory}")
|
@logger.debug("Finding all boxes in: #{@directory}")
|
||||||
@directory.children(true).each do |child|
|
@directory.children(true).each do |child|
|
||||||
# Ignore non-directories, since files are not interesting to
|
# Ignore non-directories, since files are not interesting to
|
||||||
|
@ -207,6 +212,7 @@ module Vagrant
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
results
|
results
|
||||||
end
|
end
|
||||||
|
@ -217,6 +223,7 @@ module Vagrant
|
||||||
# @Param [String] provider Provider that the box implements.
|
# @Param [String] provider Provider that the box implements.
|
||||||
# @return [Box] The box found, or `nil` if not found.
|
# @return [Box] The box found, or `nil` if not found.
|
||||||
def find(name, provider)
|
def find(name, provider)
|
||||||
|
with_collection_lock do
|
||||||
# First look directly for the box we're asking for.
|
# First look directly for the box we're asking for.
|
||||||
box_directory = @directory.join(name, provider.to_s, "metadata.json")
|
box_directory = @directory.join(name, provider.to_s, "metadata.json")
|
||||||
@logger.info("Searching for box: #{name} (#{provider}) in #{box_directory}")
|
@logger.info("Searching for box: #{name} (#{provider}) in #{box_directory}")
|
||||||
|
@ -242,6 +249,7 @@ module Vagrant
|
||||||
raise Errors::BoxUpgradeRequired, :name => name
|
raise Errors::BoxUpgradeRequired, :name => name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Didn't find it, return nil
|
# Didn't find it, return nil
|
||||||
@logger.info("Box not found: #{name} (#{provider})")
|
@logger.info("Box not found: #{name} (#{provider})")
|
||||||
|
@ -255,6 +263,7 @@ module Vagrant
|
||||||
#
|
#
|
||||||
# @return [Boolean] `true` otherwise an exception is raised.
|
# @return [Boolean] `true` otherwise an exception is raised.
|
||||||
def upgrade(name)
|
def upgrade(name)
|
||||||
|
with_collection_lock do
|
||||||
@logger.debug("Upgrade request for box: #{name}")
|
@logger.debug("Upgrade request for box: #{name}")
|
||||||
box_dir = @directory.join(name)
|
box_dir = @directory.join(name)
|
||||||
|
|
||||||
|
@ -271,6 +280,7 @@ module Vagrant
|
||||||
FileUtils.mv(temp_dir.to_s, box_dir.join("virtualbox").to_s)
|
FileUtils.mv(temp_dir.to_s, box_dir.join("virtualbox").to_s)
|
||||||
@logger.info("Box '#{name}' upgraded from V1 to V2.")
|
@logger.info("Box '#{name}' upgraded from V1 to V2.")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# We did it! Or the v1 box didn't exist so it doesn't matter.
|
# We did it! Or the v1 box didn't exist so it doesn't matter.
|
||||||
return true
|
return true
|
||||||
|
@ -330,6 +340,24 @@ module Vagrant
|
||||||
temp_dir
|
temp_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This locks the region given by the block with a lock on this
|
||||||
|
# collection.
|
||||||
|
def with_collection_lock
|
||||||
|
lock = @lock
|
||||||
|
|
||||||
|
begin
|
||||||
|
lock.synchronize {}
|
||||||
|
rescue ThreadError
|
||||||
|
# If we already hold the lock, just create a new lock so
|
||||||
|
# we definitely don't block and don't get an error.
|
||||||
|
lock = Mutex.new
|
||||||
|
end
|
||||||
|
|
||||||
|
lock.synchronize do
|
||||||
|
return yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# This is a helper that makes sure that our temporary directories
|
# This is a helper that makes sure that our temporary directories
|
||||||
# are cleaned up no matter what.
|
# are cleaned up no matter what.
|
||||||
#
|
#
|
||||||
|
|
|
@ -331,7 +331,6 @@ module Vagrant
|
||||||
# Determine the possible box formats for any boxes and find the box
|
# Determine the possible box formats for any boxes and find the box
|
||||||
box_formats = provider_options[:box_format] || provider
|
box_formats = provider_options[:box_format] || provider
|
||||||
box = nil
|
box = nil
|
||||||
box = find_box(config.vm.box, box_formats) if config.vm.box
|
|
||||||
|
|
||||||
# Set this variable in order to keep track of if the box changes
|
# Set this variable in order to keep track of if the box changes
|
||||||
# too many times.
|
# too many times.
|
||||||
|
@ -339,6 +338,8 @@ module Vagrant
|
||||||
box_changed = false
|
box_changed = false
|
||||||
|
|
||||||
load_box_and_overrides = lambda do
|
load_box_and_overrides = lambda do
|
||||||
|
box = find_box(config.vm.box, box_formats) if config.vm.box
|
||||||
|
|
||||||
# If a box was found, then we attempt to load the Vagrantfile for
|
# If a box was found, then we attempt to load the Vagrantfile for
|
||||||
# that box. We don't require a box since we allow providers to download
|
# that box. We don't require a box since we allow providers to download
|
||||||
# boxes and so on.
|
# boxes and so on.
|
||||||
|
@ -378,7 +379,6 @@ module Vagrant
|
||||||
@logger.info("Box changed to: #{config.vm.box}. Reloading configurations.")
|
@logger.info("Box changed to: #{config.vm.box}. Reloading configurations.")
|
||||||
original_box = config.vm.box
|
original_box = config.vm.box
|
||||||
box_changed = true
|
box_changed = true
|
||||||
box = find_box(config.vm.box, box_formats)
|
|
||||||
|
|
||||||
# Recurse so that we reload all the configurations
|
# Recurse so that we reload all the configurations
|
||||||
load_box_and_overrides.call
|
load_box_and_overrides.call
|
||||||
|
|
Loading…
Reference in New Issue