Store box metadata of active guest
When a guest is created, the box metadata information is stored in the machine data directory. This allows modifications to happen to the Vagrantfile definition of the box in use (box name change, box version change, etc) while still allowing the Machine instance of an active guest successfully load the box currently backing it.
This commit is contained in:
parent
9daea21c4f
commit
6c1a9dc58e
|
@ -334,7 +334,7 @@ module Vagrant
|
|||
# then look there.
|
||||
root_config = vagrantfile.config
|
||||
if opts[:machine]
|
||||
machine_info = vagrantfile.machine_config(opts[:machine], nil, nil)
|
||||
machine_info = vagrantfile.machine_config(opts[:machine], nil, nil, nil)
|
||||
root_config = machine_info[:config]
|
||||
end
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ module Vagrant
|
|||
# @return [Machine]
|
||||
def machine(name, provider, boxes, data_path, env)
|
||||
# Load the configuration for the machine
|
||||
results = machine_config(name, provider, boxes)
|
||||
results = machine_config(name, provider, boxes, data_path)
|
||||
box = results[:box]
|
||||
config = results[:config]
|
||||
config_errors = results[:config_errors]
|
||||
|
@ -109,7 +109,7 @@ module Vagrant
|
|||
# box Vagrantfile.
|
||||
# @return [Hash<Symbol, Object>] Various configuration parameters for a
|
||||
# machine. See the main documentation body for more info.
|
||||
def machine_config(name, provider, boxes)
|
||||
def machine_config(name, provider, boxes, data_path=nil)
|
||||
keys = @keys.dup
|
||||
|
||||
sub_machine = @config.vm.defined_vms[name]
|
||||
|
@ -170,6 +170,19 @@ module Vagrant
|
|||
original_box = config.vm.box
|
||||
original_version = config.vm.box_version
|
||||
|
||||
# Check if this machine has a local box metadata file
|
||||
# describing the existing guest. If so, load it and
|
||||
# set the box name and version to allow the actual
|
||||
# box in use to be discovered.
|
||||
if data_path
|
||||
meta_file = data_path.join("box_meta")
|
||||
if meta_file.file?
|
||||
box_meta = JSON.parse(meta_file.read)
|
||||
config.vm.box = box_meta["name"]
|
||||
config.vm.box_version = box_meta["box_version"]
|
||||
end
|
||||
end
|
||||
|
||||
# The proc below loads the box and provider overrides. This is
|
||||
# in a proc because it may have to recurse if the provider override
|
||||
# changes the box.
|
||||
|
@ -214,6 +227,11 @@ module Vagrant
|
|||
# Load the box and provider overrides
|
||||
load_box_proc.call
|
||||
|
||||
# Ensure box attributes are set to original values in
|
||||
# case they were modified by the local box metadata
|
||||
config.vm.box = original_box
|
||||
config.vm.box_version = original_version
|
||||
|
||||
return {
|
||||
box: box,
|
||||
provider_cls: provider_cls,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
require "json"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandUp
|
||||
# Stores metadata information about the box used
|
||||
# for the current guest. This allows Vagrant to
|
||||
# determine the box currently in use when the
|
||||
# Vagrantfile is modified with a new box name or
|
||||
# version while the guest still exists.
|
||||
class StoreBoxMetadata
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
box = env[:machine].box
|
||||
box_meta = {
|
||||
name: box.name,
|
||||
version: box.version,
|
||||
provider: box.provider,
|
||||
directory: box.directory.sub(Vagrant.user_data_path.to_s + "/", "")
|
||||
}
|
||||
meta_file = env[:machine].data_dir.join("box_meta")
|
||||
File.open(meta_file.to_s, "w+") do |file|
|
||||
file.write(JSON.dump(box_meta))
|
||||
end
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,6 +12,11 @@ module VagrantPlugins
|
|||
require File.expand_path("../command", __FILE__)
|
||||
Command
|
||||
end
|
||||
|
||||
action_hook(:store_box_metadata, :machine_action_up) do |hook|
|
||||
require_relative "middleware/store_box_metadata"
|
||||
hook.append(StoreBoxMetadata)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
require File.expand_path("../../../../../base", __FILE__)
|
||||
require Vagrant.source_root.join("plugins/commands/up/middleware/store_box_metadata")
|
||||
|
||||
describe VagrantPlugins::CommandUp::StoreBoxMetadata do
|
||||
include_context "unit"
|
||||
|
||||
let(:app) { double("app") }
|
||||
let(:machine) { double("machine", box: box) }
|
||||
let(:box) {
|
||||
double("box",
|
||||
name: box_name,
|
||||
version: box_version,
|
||||
provider: box_provider,
|
||||
directory: box_directory
|
||||
)
|
||||
}
|
||||
let(:box_name) { "BOX_NAME" }
|
||||
let(:box_version) { "1.0.0" }
|
||||
let(:box_provider) { "dummy" }
|
||||
let(:box_directory) { File.join(vagrant_user_data_path, box_directory_relative) }
|
||||
let(:box_directory_relative) { File.join("boxes", "BOX_NAME") }
|
||||
let(:vagrant_user_data_path) { "/vagrant/user/data" }
|
||||
let(:meta_path) { "META_PATH" }
|
||||
let(:env) { {machine: machine} }
|
||||
|
||||
let(:subject) { described_class.new(app, env) }
|
||||
|
||||
describe "#call" do
|
||||
|
||||
let(:meta_file) { double("meta_file") }
|
||||
|
||||
before do
|
||||
allow(Vagrant).to receive(:user_data_path).and_return(vagrant_user_data_path)
|
||||
allow(machine).to receive(:data_dir).and_return(meta_path)
|
||||
allow(meta_path).to receive(:join).with("box_meta").and_return(meta_path)
|
||||
allow(File).to receive(:open)
|
||||
expect(app).to receive(:call).with(env)
|
||||
end
|
||||
|
||||
after { subject.call(env) }
|
||||
|
||||
it "should open a metadata file" do
|
||||
expect(File).to receive(:open).with(meta_path, anything)
|
||||
end
|
||||
|
||||
context "contents of metadata file" do
|
||||
|
||||
before { expect(File).to receive(:open).with(meta_path, anything).and_yield(meta_file) }
|
||||
|
||||
it "should be JSON data" do
|
||||
expect(meta_file).to receive(:write) do |data|
|
||||
val = JSON.parse(data)
|
||||
expect(val).to be_a(Hash)
|
||||
end
|
||||
end
|
||||
|
||||
it "should include box name" do
|
||||
expect(meta_file).to receive(:write) do |data|
|
||||
val = JSON.parse(data)
|
||||
expect(val["name"]).to eq(box_name)
|
||||
end
|
||||
end
|
||||
|
||||
it "should include box version" do
|
||||
expect(meta_file).to receive(:write) do |data|
|
||||
val = JSON.parse(data)
|
||||
expect(val["version"]).to eq(box_version)
|
||||
end
|
||||
end
|
||||
|
||||
it "should include box provider" do
|
||||
expect(meta_file).to receive(:write) do |data|
|
||||
val = JSON.parse(data)
|
||||
expect(val["provider"]).to eq(box_provider)
|
||||
end
|
||||
end
|
||||
|
||||
it "should include relative box directory" do
|
||||
expect(meta_file).to receive(:write) do |data|
|
||||
val = JSON.parse(data)
|
||||
expect(val["directory"]).to eq(box_directory_relative)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -270,50 +270,50 @@ describe Vagrant::Machine do
|
|||
end
|
||||
|
||||
it "should be able to run an action that exists" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
called = false
|
||||
callable = lambda { |_env| called = true }
|
||||
|
||||
expect(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
instance.action(:up)
|
||||
instance.action(action_name)
|
||||
expect(called).to be
|
||||
end
|
||||
|
||||
it "should provide the machine in the environment" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
machine = nil
|
||||
callable = lambda { |env| machine = env[:machine] }
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
instance.action(:up)
|
||||
instance.action(action_name)
|
||||
|
||||
expect(machine).to eql(instance)
|
||||
end
|
||||
|
||||
it "should pass any extra options to the environment" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
foo = nil
|
||||
callable = lambda { |env| foo = env[:foo] }
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
instance.action(:up, foo: :bar)
|
||||
instance.action(action_name, foo: :bar)
|
||||
|
||||
expect(foo).to eq(:bar)
|
||||
end
|
||||
|
||||
it "should pass any extra options to the environment as strings" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
foo = nil
|
||||
callable = lambda { |env| foo = env["foo"] }
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
instance.action(:up, "foo" => :bar)
|
||||
instance.action(action_name, "foo" => :bar)
|
||||
|
||||
expect(foo).to eq(:bar)
|
||||
end
|
||||
|
||||
it "should return the environment as a result" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
callable = lambda { |env| env[:result] = "FOO" }
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
|
@ -323,7 +323,7 @@ describe Vagrant::Machine do
|
|||
end
|
||||
|
||||
it "should raise an exception if the action is not implemented" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(nil)
|
||||
|
||||
|
@ -332,7 +332,7 @@ describe Vagrant::Machine do
|
|||
end
|
||||
|
||||
it 'should not warn if the machines cwd has not changed' do
|
||||
initial_action_name = :up
|
||||
initial_action_name = :destroy
|
||||
second_action_name = :reload
|
||||
callable = lambda { |_env| }
|
||||
original_cwd = env.cwd.to_s
|
||||
|
@ -349,7 +349,7 @@ describe Vagrant::Machine do
|
|||
end
|
||||
|
||||
it 'should warn if the machine was last run under a different directory' do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
callable = lambda { |_env| }
|
||||
original_cwd = env.cwd.to_s
|
||||
|
||||
|
@ -374,7 +374,7 @@ describe Vagrant::Machine do
|
|||
let (:data_dir) { env.cwd }
|
||||
|
||||
it 'should not warn if vagrant is run in subdirectory' do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
callable = lambda { |_env| }
|
||||
original_cwd = env.cwd.to_s
|
||||
|
||||
|
@ -394,7 +394,7 @@ describe Vagrant::Machine do
|
|||
|
||||
context "with the vagrant-triggers community plugin" do
|
||||
it "should not call the internal trigger functions if installed" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
callable = lambda { |_env| }
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
|
@ -412,7 +412,7 @@ describe Vagrant::Machine do
|
|||
end
|
||||
|
||||
it "should call the internal trigger functions if not installed" do
|
||||
action_name = :up
|
||||
action_name = :destroy
|
||||
callable = lambda { |_env| }
|
||||
|
||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
||||
|
|
Loading…
Reference in New Issue