Merge branch 'new-state-api'

This adds a new interface for returning machine state from a provider which
allows commands like `vagrant status` to continue to properly function
in the face of differing providers.
This commit is contained in:
Mitchell Hashimoto 2013-01-21 11:43:09 -06:00
commit 294d4d5d96
22 changed files with 124 additions and 24 deletions

View File

@ -75,6 +75,7 @@ module Vagrant
autoload :Guest, 'vagrant/guest' autoload :Guest, 'vagrant/guest'
autoload :Hosts, 'vagrant/hosts' autoload :Hosts, 'vagrant/hosts'
autoload :Machine, 'vagrant/machine' autoload :Machine, 'vagrant/machine'
autoload :MachineState, 'vagrant/machine_state'
autoload :Plugin, 'vagrant/plugin' autoload :Plugin, 'vagrant/plugin'
autoload :TestHelpers, 'vagrant/test_helpers' autoload :TestHelpers, 'vagrant/test_helpers'
autoload :UI, 'vagrant/ui' autoload :UI, 'vagrant/ui'

View File

@ -248,6 +248,10 @@ module Vagrant
error_key(:machine_not_found) error_key(:machine_not_found)
end end
class MachineStateInvalid < VagrantError
error_key(:machine_state_invalid)
end
class MultiVMEnvironmentRequired < VagrantError class MultiVMEnvironmentRequired < VagrantError
status_code(5) status_code(5)
error_key(:multi_vm_required) error_key(:multi_vm_required)

View File

@ -277,7 +277,9 @@ module Vagrant
# #
# @return [Symbol] # @return [Symbol]
def state def state
@provider.state result = @provider.state
raise Errors::MachineStateInvalid if !result.is_a?(MachineState)
result
end end
protected protected

View File

@ -0,0 +1,45 @@
module Vagrant
# This represents the state of a given machine. This is a very basic
# class that simply stores a short and long description of the state
# of a machine.
#
# The state also stores a state "id" which ca be used as a unique
# identifier for a state. This should be a symbol. This allows internal
# code to compare state such as ":not_created" instead of using
# string comparison.
#
# The short description should be a single word description of the
# state of the machine such as "running" or "not created".
#
# The long description can span multiple lines describing what the
# state actually means.
class MachineState
# Unique ID for this state.
#
# @return [Symbol]
attr_reader :id
# Short description for this state.
#
# @return [String]
attr_reader :short_description
# Long description for this state.
#
# @return [String]
attr_reader :long_description
# Creates a new instance to represent the state of a machine.
#
# @param [Symbol] id Unique identifier for this state.
# @param [String] short Short (preferably one-word) description of
# the state.
# @param [String] long Long description (can span multiple lines)
# of the state.
def initialize(id, short, long)
@id = id
@short_description = short
@long_description = long
end
end
end

View File

@ -15,15 +15,15 @@ module VagrantPlugins
state = nil state = nil
results = [] results = []
with_target_vms(argv) do |machine| with_target_vms(argv) do |machine|
state = machine.state.to_s if !state state = machine.state if !state
results << "#{machine.name.to_s.ljust(25)}#{machine.state.to_s.gsub("_", " ")}" results << "#{machine.name.to_s.ljust(25)}#{machine.state.short_description}"
end end
state = results.length == 1 ? state : "listing" state = results.length == 1 ? state : "listing"
@env.ui.info(I18n.t("vagrant.commands.status.output", @env.ui.info(I18n.t("vagrant.commands.status.output",
:states => results.join("\n"), :states => results.join("\n"),
:message => I18n.t("vagrant.commands.status.#{state}")), :message => state.long_description),
:prefix => false) :prefix => false)
# Success, exit status 0 # Success, exit status 0

View File

@ -34,7 +34,7 @@ module VagrantPlugins
# If the VM is not starting or running, something went wrong # If the VM is not starting or running, something went wrong
# and we need to show a useful error. # and we need to show a useful error.
state = @env[:machine].provider.state state = @env[:machine].provider.state.id
raise Errors::VMFailedToRun if state != :starting && state != :running raise Errors::VMFailedToRun if state != :starting && state != :running
sleep 2 if !@env["vagrant.test"] sleep 2 if !@env["vagrant.test"]

View File

@ -7,7 +7,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].state == :inaccessible if env[:machine].state.id == :inaccessible
# The VM we are attempting to manipulate is inaccessible. This # The VM we are attempting to manipulate is inaccessible. This
# is a very bad situation and can only be fixed by the user. It # is a very bad situation and can only be fixed by the user. It
# also prohibits us from actually doing anything with the virtual # also prohibits us from actually doing anything with the virtual

View File

@ -9,7 +9,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].state == :not_created if env[:machine].state.id == :not_created
raise Vagrant::Errors::VMNotCreatedError raise Vagrant::Errors::VMNotCreatedError
end end

View File

@ -9,7 +9,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].state != :running if env[:machine].state.id != :running
raise Vagrant::Errors::VMNotRunningError raise Vagrant::Errors::VMNotRunningError
end end

View File

@ -8,7 +8,7 @@ module VagrantPlugins
def call(env) def call(env)
# Set the result to be true if the machine is created. # Set the result to be true if the machine is created.
env[:result] = env[:machine].state != :not_created env[:result] = env[:machine].state.id != :not_created
# Call the next if we have one (but we shouldn't, since this # Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares) # middleware is built to run with the Call-type middlewares)

View File

@ -7,7 +7,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].provider.state == :saved if env[:machine].provider.state.id == :saved
env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding") env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding")
env[:machine].provider.driver.discard_saved_state env[:machine].provider.driver.discard_saved_state
end end

View File

@ -13,7 +13,8 @@ module VagrantPlugins
def call(env) def call(env)
@env = env @env = env
raise Vagrant::Errors::VMPowerOffToPackage if @env[:machine].provider.state != :poweroff raise Vagrant::Errors::VMPowerOffToPackage if \
@env[:machine].provider.state.id != :poweroff
setup_temp_dir setup_temp_dir
export export

View File

@ -7,7 +7,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
current_state = env[:machine].provider.state current_state = env[:machine].provider.state.id
if current_state == :running || current_state == :gurumeditation if current_state == :running || current_state == :gurumeditation
# If the VM is running and we're not forcing, we can # If the VM is running and we're not forcing, we can
# attempt a graceful shutdown # attempt a graceful shutdown
@ -17,7 +17,7 @@ module VagrantPlugins
end end
# If we're not powered off now, then force it # If we're not powered off now, then force it
if env[:machine].provider.state != :poweroff if env[:machine].provider.state.id != :poweroff
env[:ui].info I18n.t("vagrant.actions.vm.halt.force") env[:ui].info I18n.t("vagrant.actions.vm.halt.force")
env[:machine].provider.driver.halt env[:machine].provider.driver.halt
end end

View File

@ -33,7 +33,7 @@ module VagrantPlugins
end end
def recover(env) def recover(env)
if env[:machine].provider.state != :not_created if env[:machine].provider.state.id != :not_created
return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
# Interrupted, destroy the VM. We note that we don't want to # Interrupted, destroy the VM. We note that we don't want to

View File

@ -8,7 +8,7 @@ module VagrantPlugins
def call(env) def call(env)
# Set the result to be true if the machine is running. # Set the result to be true if the machine is running.
env[:result] = env[:machine].state == :running env[:result] = env[:machine].state.id == :running
# Call the next if we have one (but we shouldn't, since this # Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares) # middleware is built to run with the Call-type middlewares)

View File

@ -8,7 +8,7 @@ module VagrantPlugins
def call(env) def call(env)
# Set the result to be true if the machine is saved. # Set the result to be true if the machine is saved.
env[:result] = env[:machine].state == :saved env[:result] = env[:machine].state.id == :saved
# Call the next if we have one (but we shouldn't, since this # Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares) # middleware is built to run with the Call-type middlewares)

View File

@ -7,7 +7,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].provider.state == :saved if env[:machine].provider.state.id == :saved
env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming") env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming")
env[:action_runner].run(Boot, env) env[:action_runner].run(Boot, env)
end end

View File

@ -7,7 +7,7 @@ module VagrantPlugins
end end
def call(env) def call(env)
if env[:machine].provider.state == :running if env[:machine].provider.state.id == :running
env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending") env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending")
env[:machine].provider.driver.suspend env[:machine].provider.driver.suspend
end end

View File

@ -63,10 +63,19 @@ module VagrantPlugins
def state def state
# XXX: What happens if we destroy the VM but the UUID is still # XXX: What happens if we destroy the VM but the UUID is still
# set here? # set here?
return :not_created if !@driver.uuid
state = @driver.read_state # Determine the ID of the state here.
return :unknown if !state state_id = nil
state state_id = :not_created if !@driver.uuid
state_id = @driver.read_state if !state_id
state_id = :unknown if !state_id
# Translate into short/long descriptions
short = state_id.to_s.gsub("_", " ")
long = I18n.t("vagrant.commands.status.#{state_id}")
# Return the state
Vagrant::MachineState.new(state_id, short, long)
end end
# Returns a human-friendly string version of this provider which # Returns a human-friendly string version of this provider which

View File

@ -145,6 +145,12 @@ en:
machine_not_found: |- machine_not_found: |-
The machine with the name '%{name}' was not found configured for The machine with the name '%{name}' was not found configured for
this Vagrant environment. this Vagrant environment.
machine_state_invalid: |-
An internal error has occurred! The provider of the machine you're
trying to work with reported an invalid state. This is a bug with
the provider you're using, and not with Vagrant itself or with
any configuration you may have done. Please report this bug to
the proper location.
multi_vm_required: |- multi_vm_required: |-
A multi-vm environment is required for name specification to this command. A multi-vm environment is required for name specification to this command.
multi_vm_target_required: |- multi_vm_target_required: |-

View File

@ -0,0 +1,26 @@
require "pathname"
require File.expand_path("../../base", __FILE__)
describe Vagrant::MachineState do
include_context "unit"
let(:id) { :some_state }
let(:short) { "foo" }
let(:long) { "I am a longer foo" }
it "should give access to the id" do
instance = described_class.new(id, short, long)
instance.id.should == id
end
it "should give access to the short description" do
instance = described_class.new(id, short, long)
instance.short_description.should == short
end
it "should give access to the long description" do
instance = described_class.new(id, short, long)
instance.long_description.should == long
end
end

View File

@ -385,10 +385,16 @@ describe Vagrant::Machine do
describe "state" do describe "state" do
it "should query state from the provider" do it "should query state from the provider" do
state = :running state = Vagrant::MachineState.new(:id, "short", "long")
provider.should_receive(:state).and_return(state) provider.should_receive(:state).and_return(state)
instance.state.should == state instance.state.id.should == :id
end
it "should raise an exception if a MachineState is not returned" do
provider.should_receive(:state).and_return(:old_school)
expect { instance.state }.
to raise_error(Vagrant::Errors::MachineStateInvalid)
end end
end end
end end