diff --git a/lib/vagrant/machine_index.rb b/lib/vagrant/machine_index.rb index 64a26666b..42ea7328a 100644 --- a/lib/vagrant/machine_index.rb +++ b/lib/vagrant/machine_index.rb @@ -171,27 +171,40 @@ module Vagrant id = entry.id @lock.synchronize do - # Verify the machine is locked so we can safely write - # to it. - if !id - id = SecureRandom.uuid.gsub("-", "") - lock_file = lock_machine(id) - if !lock_file - raise "Failed to lock new machine: #{entry.name}" + with_index_lock do + # Reload so we have the latest machine data. This allows other + # processes to update their own machines without conflicting + # with our own. + unlocked_reload + + # If we don't have a machine ID, try to look one up + if !id + self.each do |other| + if entry.name == other.name && + entry.provider == other.provider && + entry.vagrantfile_path.to_s == other.vagrantfile_path.to_s + id = other.id + break + end + end + + # If we still don't have an ID, generate a random one + id = SecureRandom.uuid.gsub("-", "") if !id + + # Get a lock on this machine + lock_file = lock_machine(id) + if !lock_file + raise "Failed to lock new machine: #{entry.name}" + end + + @machine_locks[id] = lock_file end - @machine_locks[id] = lock_file - end + if !@machine_locks[id] + raise "Unlocked write on machine: #{id}" + end - if !@machine_locks[id] - raise "Unlocked write on machine: #{id}" - end - - with_index_lock do - # Reload so we have the latest machine data, then update - # this particular machine, then write. This allows other processes - # to update their own machines without conflicting with our own. - unlocked_reload + # Set our machine and save @machines[id] = struct unlocked_save end @@ -218,6 +231,8 @@ module Vagrant # # If the lock cannot be acquired, then nil is returned. # + # This should be called within an index lock. + # # @return [File] def lock_machine(uuid) lock_path = @data_dir.join("#{uuid}.lock") diff --git a/test/unit/vagrant/machine_index_test.rb b/test/unit/vagrant/machine_index_test.rb index 58eed8896..69a753052 100644 --- a/test/unit/vagrant/machine_index_test.rb +++ b/test/unit/vagrant/machine_index_test.rb @@ -227,5 +227,39 @@ describe Vagrant::MachineIndex do "foo" => "bar", }) end + + it "updates an existing directory if the name, provider, and path are the same" do + entry = entry_klass.new + entry.name = "foo" + entry.provider = "bar" + entry.vagrantfile_path = "/bar" + entry.state = "foo" + + result = subject.set(entry) + expect(result.id).to_not be_empty + + # Release it so we can modify it + subject.release(result) + + entry2 = entry_klass.new + entry2.name = entry.name + entry2.provider = entry.provider + entry2.vagrantfile_path = entry.vagrantfile_path + entry2.state = "bar" + expect(entry2.id).to be_nil + + nextresult = subject.set(entry2) + expect(nextresult.id).to eq(result.id) + + # Release it so we can test the contents + subject.release(nextresult) + + # Get it from a new class and check the results + subject = described_class.new(data_dir) + entry = subject.get(result.id) + expect(entry).to_not be_nil + expect(entry.name).to eq(entry2.name) + expect(entry.state).to eq(entry2.state) + end end end