Upgrade V1-style dotfile to V2
See the code and comments for details on how this is done. As usual, we are very careful about this so as not to inadvertently destruct real user data.
This commit is contained in:
parent
495479f480
commit
4e649cc987
|
@ -1,4 +1,5 @@
|
|||
require 'fileutils'
|
||||
require 'json'
|
||||
require 'pathname'
|
||||
require 'set'
|
||||
|
||||
|
@ -517,7 +518,61 @@ module Vagrant
|
|||
#
|
||||
# @param [Pathname] path The path to the dotfile
|
||||
def upgrade_v1_dotfile(path)
|
||||
raise "V1 environment detected. An auto-upgrade process will be made soon."
|
||||
@logger.info("Upgrading V1 dotfile to V2 directory structure...")
|
||||
|
||||
# First, verify the file isn't empty. If it is an empty file, we
|
||||
# just delete it and go on with life.
|
||||
contents = path.read.strip
|
||||
if contents.strip == ""
|
||||
@logger.info("V1 dotfile was empty. Removing and moving on.")
|
||||
path.delete
|
||||
return
|
||||
end
|
||||
|
||||
# Otherwise, verify there is valid JSON in here since a Vagrant
|
||||
# environment would always ensure valid JSON. This is a sanity check
|
||||
# to make sure we don't nuke a dotfile that is not ours...
|
||||
@logger.debug("Attempting to parse JSON of V1 file")
|
||||
json_data = nil
|
||||
begin
|
||||
json_data = JSON.parse(contents)
|
||||
@logger.debug("JSON parsed successfully. Things are okay.")
|
||||
rescue JSON::ParserError
|
||||
# The file could've been tampered with since Vagrant 1.0.x is
|
||||
# supposed to ensure that the contents are valid JSON. Show an error.
|
||||
raise Errors::DotfileUpgradeJSONError,
|
||||
:state_file => path.to_s
|
||||
end
|
||||
|
||||
# Alright, let's upgrade this guy to the new structure. Start by
|
||||
# backing up the old dotfile.
|
||||
backup_file = path.dirname.join(".vagrant.v1.#{Time.now.to_i}")
|
||||
@logger.info("Renaming old dotfile to: #{backup_file}")
|
||||
path.rename(backup_file)
|
||||
|
||||
# Now, we create the actual local data directory. This should succeed
|
||||
# this time since we renamed the old conflicting V1.
|
||||
setup_local_data_path
|
||||
|
||||
if json_data["active"]
|
||||
@logger.debug("Upgrading to V2 style for each active VM")
|
||||
json_data["active"].each do |name, id|
|
||||
@logger.info("Upgrading dotfile: #{name} (#{id})")
|
||||
|
||||
# Create the machine configuration directory
|
||||
directory = @local_data_path.join("machines/#{name}/virtualbox")
|
||||
FileUtils.mkdir_p(directory)
|
||||
|
||||
# Write the ID file
|
||||
directory.join("id").open("w+") do |f|
|
||||
f.write(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Upgrade complete! Let the user know
|
||||
@ui.info(I18n.t("vagrant.general.upgraded_v1_dotfile",
|
||||
:backup_path => backup_file.to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -166,6 +166,10 @@ module Vagrant
|
|||
error_key(:dotfile_is_directory)
|
||||
end
|
||||
|
||||
class DotfileUpgradeJSONError < VagrantError
|
||||
error_key(:dotfile_upgrade_json_error)
|
||||
end
|
||||
|
||||
class DownloaderFileDoesntExist < VagrantError
|
||||
status_code(37)
|
||||
error_key(:file_missing, "vagrant.downloaders.file")
|
||||
|
|
|
@ -9,6 +9,17 @@ en:
|
|||
|
||||
Old: %{old}
|
||||
New: %{new}
|
||||
upgraded_v1_dotfile: |-
|
||||
A Vagrant 1.0.x state file was found for this environment. Vagrant has
|
||||
gone ahead and auto-upgraded this to the latest format. Everything
|
||||
should continue working as normal. Beware, however, that older versions
|
||||
of Vagrant may no longer be used with this environment.
|
||||
|
||||
However, in case anything went wrong, the old dotfile was backed up
|
||||
to the location below. If everything is okay, it is safe to remove
|
||||
this backup.
|
||||
|
||||
Backup: %{backup_path}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Translations for exception classes
|
||||
|
@ -55,6 +66,24 @@ en:
|
|||
this command in another directory. If you aren't in a home directory,
|
||||
then please rename ".vagrant" to something else, or configure Vagrant
|
||||
to use another filename by modifying `config.vagrant.dotfile_name`.
|
||||
dotfile_upgrade_json_error: |-
|
||||
A Vagrant 1.0.x local state file was found. Vagrant is able to upgrade
|
||||
this to the latest format automatically, however various checks are
|
||||
put in place to verify data isn't incorrectly deleted. In this case,
|
||||
the old state file was not valid JSON. Vagrant 1.0.x would store state
|
||||
as valid JSON, meaning that this file was probably tampered with or
|
||||
manually edited. Vagrant's auto-upgrade process cannot continue in this
|
||||
case.
|
||||
|
||||
In most cases, this can be resolve by simply removing the state file.
|
||||
Note however though that if Vagrant was previously managing virtual
|
||||
machines, they may be left in an "orphan" state. That is, if they are
|
||||
running or exist, they'll have to manually be removed.
|
||||
|
||||
If you're unsure what to do, ask the Vagrant mailing list or contact
|
||||
support.
|
||||
|
||||
State file path: %{state_file}
|
||||
environment_locked: |-
|
||||
An instance of Vagrant is already running. Only one instance of Vagrant
|
||||
may run at any given time to avoid problems with VirtualBox inconsistencies
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
require File.expand_path("../../base", __FILE__)
|
||||
require "json"
|
||||
require "pathname"
|
||||
require "tempfile"
|
||||
|
||||
require "vagrant/util/file_mode"
|
||||
|
||||
|
@ -91,6 +93,55 @@ describe Vagrant::Environment do
|
|||
instance = described_class.new(:local_data_path => dir)
|
||||
instance.local_data_path.to_s.should == dir
|
||||
end
|
||||
|
||||
describe "upgrading V1 dotfiles" do
|
||||
let(:v1_dotfile_tempfile) { Tempfile.new("vagrant") }
|
||||
let(:v1_dotfile) { Pathname.new(v1_dotfile_tempfile.path) }
|
||||
let(:local_data_path) { v1_dotfile_tempfile.path }
|
||||
let(:instance) { described_class.new(:local_data_path => local_data_path) }
|
||||
|
||||
it "should be fine if dotfile is empty" do
|
||||
v1_dotfile.open("w+") do |f|
|
||||
f.write("")
|
||||
end
|
||||
|
||||
expect { instance }.to_not raise_error
|
||||
Pathname.new(local_data_path).should be_directory
|
||||
end
|
||||
|
||||
it "should upgrade all active VMs" do
|
||||
active_vms = {
|
||||
"foo" => "foo_id",
|
||||
"bar" => "bar_id"
|
||||
}
|
||||
|
||||
v1_dotfile.open("w+") do |f|
|
||||
f.write(JSON.dump({
|
||||
"active" => active_vms
|
||||
}))
|
||||
end
|
||||
|
||||
expect { instance }.to_not raise_error
|
||||
|
||||
local_data_pathname = Pathname.new(local_data_path)
|
||||
foo_id_file = local_data_pathname.join("machines/foo/virtualbox/id")
|
||||
foo_id_file.should be_file
|
||||
foo_id_file.read.should == "foo_id"
|
||||
|
||||
bar_id_file = local_data_pathname.join("machines/bar/virtualbox/id")
|
||||
bar_id_file.should be_file
|
||||
bar_id_file.read.should == "bar_id"
|
||||
end
|
||||
|
||||
it "should raise an error if invalid JSON" do
|
||||
v1_dotfile.open("w+") do |f|
|
||||
f.write("bad")
|
||||
end
|
||||
|
||||
expect { instance }.
|
||||
to raise_error(Vagrant::Errors::DotfileUpgradeJSONError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "default provider" do
|
||||
|
@ -239,7 +290,7 @@ VF
|
|||
|
||||
it "should return a machine object with the machine configuration" do
|
||||
# Create a provider
|
||||
foo_config = Class.new do
|
||||
foo_config = Class.new(Vagrant.plugin("2", :config)) do
|
||||
attr_accessor :value
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue