Merge pull request #10615 from briancain/introduce-typed-triggers
Introduce :type option for Vagrant triggers
This commit is contained in:
commit
0bc0bdd616
|
@ -0,0 +1,31 @@
|
||||||
|
module Vagrant
|
||||||
|
module Action
|
||||||
|
module Builtin
|
||||||
|
# This class is intended to be used by the Action::Warden class for executing
|
||||||
|
# action triggers before any given action.
|
||||||
|
#
|
||||||
|
# @param [Symbol] action_name - name to fire trigger on
|
||||||
|
# @param [Vagrant::Plugin::V2::Triger] triggers - trigger object
|
||||||
|
class AfterTriggerAction
|
||||||
|
# @param [Symbol] action_name - The action class name to fire trigger on
|
||||||
|
# @param [Vagrant::Plugin::V2::Triger] triggers - trigger object
|
||||||
|
def initialize(app, env, action_name, triggers)
|
||||||
|
@app = app
|
||||||
|
@env = env
|
||||||
|
@triggers = triggers
|
||||||
|
@action_name = action_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
machine = env[:machine]
|
||||||
|
machine_name = machine.name if machine
|
||||||
|
|
||||||
|
@triggers.fire_triggers(@action_name, :after, machine_name, :action) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers");
|
||||||
|
|
||||||
|
# Carry on
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
module Vagrant
|
||||||
|
module Action
|
||||||
|
module Builtin
|
||||||
|
# This class is intended to be used by the Action::Warden class for executing
|
||||||
|
# action triggers before any given action.
|
||||||
|
class BeforeTriggerAction
|
||||||
|
# @param [Symbol] action_name - The action class name to fire trigger on
|
||||||
|
# @param [Vagrant::Plugin::V2::Triger] triggers - trigger object
|
||||||
|
def initialize(app, env, action_name, triggers)
|
||||||
|
@app = app
|
||||||
|
@env = env
|
||||||
|
@triggers = triggers
|
||||||
|
@action_name = action_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
machine = env[:machine]
|
||||||
|
machine_name = machine.name if machine
|
||||||
|
|
||||||
|
@triggers.fire_triggers(@action_name, :before, machine_name, :action) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers");
|
||||||
|
|
||||||
|
# Carry on
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,7 @@ require 'log4r'
|
||||||
|
|
||||||
require 'vagrant/action/hook'
|
require 'vagrant/action/hook'
|
||||||
require 'vagrant/util/busy'
|
require 'vagrant/util/busy'
|
||||||
|
require 'vagrant/util/experimental'
|
||||||
|
|
||||||
module Vagrant
|
module Vagrant
|
||||||
module Action
|
module Action
|
||||||
|
@ -33,6 +34,19 @@ module Vagrant
|
||||||
environment.merge!(@lazy_globals.call) if @lazy_globals
|
environment.merge!(@lazy_globals.call) if @lazy_globals
|
||||||
environment.merge!(options || {})
|
environment.merge!(options || {})
|
||||||
|
|
||||||
|
if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
|
# NOTE: Triggers are initialized later in the Action::Runer because of
|
||||||
|
# how `@lazy_globals` are evaluated. Rather than trying to guess where
|
||||||
|
# the `env` is coming from, we can wait until they're merged into a single
|
||||||
|
# hash above.
|
||||||
|
env = environment[:env]
|
||||||
|
machine = environment[:machine]
|
||||||
|
machine_name = machine.name if machine
|
||||||
|
|
||||||
|
ui = Vagrant::UI::Prefixed.new(env.ui, "vargant")
|
||||||
|
triggers = Vagrant::Plugin::V2::Trigger.new(env, env.vagrantfile.config.trigger, machine, ui)
|
||||||
|
end
|
||||||
|
|
||||||
# Setup the action hooks
|
# Setup the action hooks
|
||||||
hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name])
|
hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name])
|
||||||
if !hooks.empty?
|
if !hooks.empty?
|
||||||
|
@ -61,10 +75,16 @@ module Vagrant
|
||||||
@@reported_interrupt = true
|
@@reported_interrupt = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
action_name = environment[:action_name]
|
||||||
|
|
||||||
|
triggers.fire_triggers(action_name, :before, machine_name, :hook) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
|
|
||||||
# We place a process lock around every action that is called
|
# We place a process lock around every action that is called
|
||||||
@logger.info("Running action: #{environment[:action_name]} #{callable_id}")
|
@logger.info("Running action: #{environment[:action_name]} #{callable_id}")
|
||||||
Util::Busy.busy(int_callback) { callable.call(environment) }
|
Util::Busy.busy(int_callback) { callable.call(environment) }
|
||||||
|
|
||||||
|
triggers.fire_triggers(action_name, :after, machine_name, :hook) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
|
|
||||||
# Return the environment in case there are things in there that
|
# Return the environment in case there are things in there that
|
||||||
# the caller wants to use.
|
# the caller wants to use.
|
||||||
environment
|
environment
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
require "log4r"
|
require "log4r"
|
||||||
|
require 'vagrant/util/experimental'
|
||||||
|
require 'vagrant/action/builtin/before_trigger'
|
||||||
|
require 'vagrant/action/builtin/after_trigger'
|
||||||
|
|
||||||
module Vagrant
|
module Vagrant
|
||||||
module Action
|
module Action
|
||||||
|
@ -16,8 +19,21 @@ module Vagrant
|
||||||
attr_accessor :actions, :stack
|
attr_accessor :actions, :stack
|
||||||
|
|
||||||
def initialize(actions, env)
|
def initialize(actions, env)
|
||||||
|
if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
|
if env[:trigger_env]
|
||||||
|
@env = env[:trigger_env]
|
||||||
|
else
|
||||||
|
@env = env[:env]
|
||||||
|
end
|
||||||
|
|
||||||
|
machine = env[:machine]
|
||||||
|
machine_name = machine.name if machine
|
||||||
|
ui = Vagrant::UI::Prefixed.new(@env.ui, "vargant")
|
||||||
|
@triggers = Vagrant::Plugin::V2::Trigger.new(@env, @env.vagrantfile.config.trigger, machine, ui)
|
||||||
|
end
|
||||||
|
|
||||||
@stack = []
|
@stack = []
|
||||||
@actions = actions.map { |m| finalize_action(m, env) }
|
@actions = actions.map { |m| finalize_action(m, env) }.flatten
|
||||||
@logger = Log4r::Logger.new("vagrant::action::warden")
|
@logger = Log4r::Logger.new("vagrant::action::warden")
|
||||||
@last_error = nil
|
@last_error = nil
|
||||||
end
|
end
|
||||||
|
@ -87,7 +103,17 @@ module Vagrant
|
||||||
if klass.is_a?(Class)
|
if klass.is_a?(Class)
|
||||||
# A action klass which is to be instantiated with the
|
# A action klass which is to be instantiated with the
|
||||||
# app, env, and any arguments given
|
# app, env, and any arguments given
|
||||||
klass.new(self, env, *args, &block)
|
|
||||||
|
# We wrap the action class in two Trigger method calls so that
|
||||||
|
# action triggers can fire before and after each given action in the stack.
|
||||||
|
klass_name = klass.name
|
||||||
|
[Vagrant::Action::Builtin::BeforeTriggerAction.new(self, env,
|
||||||
|
klass_name,
|
||||||
|
@triggers),
|
||||||
|
klass.new(self, env, *args, &block),
|
||||||
|
Vagrant::Action::Builtin::AfterTriggerAction.new(self, env,
|
||||||
|
klass_name,
|
||||||
|
@triggers)]
|
||||||
elsif klass.respond_to?(:call)
|
elsif klass.respond_to?(:call)
|
||||||
# Make it a lambda which calls the item then forwards
|
# Make it a lambda which calls the item then forwards
|
||||||
# up the chain
|
# up the chain
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
require 'log4r'
|
require 'log4r'
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
|
|
||||||
|
require 'vagrant/util/experimental'
|
||||||
|
|
||||||
module Vagrant
|
module Vagrant
|
||||||
# Manages the command line interface to Vagrant.
|
# Manages the command line interface to Vagrant.
|
||||||
class CLI < Vagrant.plugin("2", :command)
|
class CLI < Vagrant.plugin("2", :command)
|
||||||
|
@ -11,6 +13,11 @@ module Vagrant
|
||||||
@logger = Log4r::Logger.new("vagrant::cli")
|
@logger = Log4r::Logger.new("vagrant::cli")
|
||||||
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
|
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
|
||||||
|
|
||||||
|
if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
|
ui = Vagrant::UI::Prefixed.new(env.ui, "vargant")
|
||||||
|
@triggers = Vagrant::Plugin::V2::Trigger.new(env, env.vagrantfile.config.trigger, nil, ui)
|
||||||
|
end
|
||||||
|
|
||||||
Util::CheckpointClient.instance.setup(env).check
|
Util::CheckpointClient.instance.setup(env).check
|
||||||
@logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}")
|
@logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}")
|
||||||
end
|
end
|
||||||
|
@ -55,7 +62,9 @@ module Vagrant
|
||||||
# Initialize and execute the command class, returning the exit status.
|
# Initialize and execute the command class, returning the exit status.
|
||||||
result = 0
|
result = 0
|
||||||
begin
|
begin
|
||||||
|
@triggers.fire_triggers(@sub_command.to_sym, :before, nil, :command) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
result = command_class.new(@sub_args, @env).execute
|
result = command_class.new(@sub_args, @env).execute
|
||||||
|
@triggers.fire_triggers(@sub_command.to_sym, :after, nil, :command) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||||
rescue Interrupt
|
rescue Interrupt
|
||||||
@env.ui.info(I18n.t("vagrant.cli_interrupt"))
|
@env.ui.info(I18n.t("vagrant.cli_interrupt"))
|
||||||
result = 1
|
result = 1
|
||||||
|
|
|
@ -210,7 +210,8 @@ module Vagrant
|
||||||
home_path: home_path,
|
home_path: home_path,
|
||||||
root_path: root_path,
|
root_path: root_path,
|
||||||
tmp_path: tmp_path,
|
tmp_path: tmp_path,
|
||||||
ui: @ui
|
ui: @ui,
|
||||||
|
env: self
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -804,6 +804,10 @@ module Vagrant
|
||||||
error_key(:triggers_bad_exit_codes)
|
error_key(:triggers_bad_exit_codes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class TriggersGuestNotExist < VagrantError
|
||||||
|
error_key(:triggers_guest_not_exist)
|
||||||
|
end
|
||||||
|
|
||||||
class TriggersGuestNotRunning < VagrantError
|
class TriggersGuestNotRunning < VagrantError
|
||||||
error_key(:triggers_guest_not_running)
|
error_key(:triggers_guest_not_running)
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,7 +110,7 @@ module Vagrant
|
||||||
@ui = Vagrant::UI::Prefixed.new(@env.ui, @name)
|
@ui = Vagrant::UI::Prefixed.new(@env.ui, @name)
|
||||||
@ui_mutex = Mutex.new
|
@ui_mutex = Mutex.new
|
||||||
@state_mutex = Mutex.new
|
@state_mutex = Mutex.new
|
||||||
@triggers = Vagrant::Plugin::V2::Trigger.new(@env, @config.trigger, self)
|
@triggers = Vagrant::Plugin::V2::Trigger.new(@env, @config.trigger, self, @ui)
|
||||||
|
|
||||||
# Read the ID, which is usually in local storage
|
# Read the ID, which is usually in local storage
|
||||||
@id = nil
|
@id = nil
|
||||||
|
@ -160,10 +160,7 @@ module Vagrant
|
||||||
# as extra data set on the environment hash for the middleware
|
# as extra data set on the environment hash for the middleware
|
||||||
# runner.
|
# runner.
|
||||||
def action(name, opts=nil)
|
def action(name, opts=nil)
|
||||||
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
|
@triggers.fire_triggers(name, :before, @name.to_s, :action)
|
||||||
if !plugins.keys.include?("vagrant-triggers")
|
|
||||||
@triggers.fire_triggers(name, :before, @name.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
@logger.info("Calling action: #{name} on provider #{@provider}")
|
@logger.info("Calling action: #{name} on provider #{@provider}")
|
||||||
|
|
||||||
|
@ -175,6 +172,10 @@ module Vagrant
|
||||||
|
|
||||||
# Extra env keys are the remaining opts
|
# Extra env keys are the remaining opts
|
||||||
extra_env = opts.dup
|
extra_env = opts.dup
|
||||||
|
# An environment is required for triggers to function properly. This is
|
||||||
|
# passed in specifically for the `#Action::Warden` class triggers. We call it
|
||||||
|
# `:trigger_env` instead of `env` in case it collides with an existing environment
|
||||||
|
extra_env[:trigger_env] = @env
|
||||||
|
|
||||||
check_cwd # Warns the UI if the machine was last used on a different dir
|
check_cwd # Warns the UI if the machine was last used on a different dir
|
||||||
|
|
||||||
|
@ -210,9 +211,7 @@ module Vagrant
|
||||||
action_result
|
action_result
|
||||||
end
|
end
|
||||||
|
|
||||||
if !plugins.keys.include?("vagrant-triggers")
|
@triggers.fire_triggers(name, :after, @name.to_s, :action)
|
||||||
@triggers.fire_triggers(name, :after, @name.to_s)
|
|
||||||
end
|
|
||||||
# preserve returning environment after machine action runs
|
# preserve returning environment after machine action runs
|
||||||
return return_env
|
return return_env
|
||||||
rescue Errors::EnvironmentLockedError
|
rescue Errors::EnvironmentLockedError
|
||||||
|
|
|
@ -20,20 +20,35 @@ module Vagrant
|
||||||
# @param [Vagrant::Environment] env Vagrant environment
|
# @param [Vagrant::Environment] env Vagrant environment
|
||||||
# @param [Kernel_V2::TriggerConfig] config Trigger configuration
|
# @param [Kernel_V2::TriggerConfig] config Trigger configuration
|
||||||
# @param [Vagrant::Machine] machine Active Machine
|
# @param [Vagrant::Machine] machine Active Machine
|
||||||
def initialize(env, config, machine)
|
# @param [Vagrant::UI] ui Class for printing messages to user
|
||||||
|
def initialize(env, config, machine, ui)
|
||||||
@env = env
|
@env = env
|
||||||
@config = config
|
@config = config
|
||||||
@machine = machine
|
@machine = machine
|
||||||
|
@ui = ui
|
||||||
|
|
||||||
@logger = Log4r::Logger.new("vagrant::trigger::#{self.class.to_s.downcase}")
|
@logger = Log4r::Logger.new("vagrant::trigger::#{self.class.to_s.downcase}")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fires all triggers, if any are defined for the action and guest
|
# Fires all triggers, if any are defined for the action and guest. Returns early
|
||||||
|
# and logs a warning if the community plugin `vagrant-triggers` is installed
|
||||||
#
|
#
|
||||||
# @param [Symbol] action Vagrant command to fire trigger on
|
# @param [Symbol] action Vagrant command to fire trigger on
|
||||||
# @param [Symbol] stage :before or :after
|
# @param [Symbol] stage :before or :after
|
||||||
# @param [String] guest_name The guest that invoked firing the triggers
|
# @param [String] guest_name The guest that invoked firing the triggers
|
||||||
def fire_triggers(action, stage, guest_name)
|
def fire_triggers(action, stage, guest_name, type)
|
||||||
|
if community_plugin_detected?
|
||||||
|
@logger.warn("Community plugin `vagrant-triggers detected, so core triggers will not fire")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if !action
|
||||||
|
@logger.warn("Action given is nil, no triggers will fire")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
action = action.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
# get all triggers matching action
|
# get all triggers matching action
|
||||||
triggers = []
|
triggers = []
|
||||||
if stage == :before
|
if stage == :before
|
||||||
|
@ -51,27 +66,41 @@ module Vagrant
|
||||||
guest_name: guest_name
|
guest_name: guest_name
|
||||||
end
|
end
|
||||||
|
|
||||||
triggers = filter_triggers(triggers, guest_name)
|
triggers = filter_triggers(triggers, guest_name, type)
|
||||||
|
|
||||||
if !triggers.empty?
|
if !triggers.empty?
|
||||||
@logger.info("Firing trigger for action #{action} on guest #{guest_name}")
|
@logger.info("Firing trigger for action #{action} on guest #{guest_name}")
|
||||||
@machine.ui.info(I18n.t("vagrant.trigger.start", stage: stage, action: action))
|
@ui.info(I18n.t("vagrant.trigger.start", type: type, stage: stage, action: action))
|
||||||
fire(triggers, guest_name)
|
fire(triggers, guest_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
# Internal methods, don't call these.
|
# Internal methods, don't call these.
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Looks up if the community plugin `vagrant-triggers` is installed
|
||||||
|
# and also caches the result
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
def community_plugin_detected?
|
||||||
|
if !defined?(@_triggers_enabled)
|
||||||
|
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
|
||||||
|
@_triggers_enabled = plugins.keys.include?("vagrant-triggers")
|
||||||
|
end
|
||||||
|
@_triggers_enabled
|
||||||
|
end
|
||||||
|
|
||||||
# Filters triggers to be fired based on configured restraints
|
# Filters triggers to be fired based on configured restraints
|
||||||
#
|
#
|
||||||
# @param [Array] triggers An array of triggers to be filtered
|
# @param [Array] triggers An array of triggers to be filtered
|
||||||
# @param [String] guest_name The name of the current guest
|
# @param [String] guest_name The name of the current guest
|
||||||
|
# @param [Symbol] type The type of trigger (:command or :type)
|
||||||
# @return [Array] The filtered array of triggers
|
# @return [Array] The filtered array of triggers
|
||||||
def filter_triggers(triggers, guest_name)
|
def filter_triggers(triggers, guest_name, type)
|
||||||
# look for only_on trigger constraint and if it doesn't match guest
|
# look for only_on trigger constraint and if it doesn't match guest
|
||||||
# name, throw it away also be sure to preserve order
|
# name, throw it away also be sure to preserve order
|
||||||
filter = triggers.dup
|
filter = triggers.dup
|
||||||
|
@ -91,6 +120,10 @@ module Vagrant
|
||||||
index = triggers.index(trigger) unless match == true
|
index = triggers.index(trigger) unless match == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if trigger.type != type
|
||||||
|
index = triggers.index(trigger)
|
||||||
|
end
|
||||||
|
|
||||||
if index
|
if index
|
||||||
@logger.debug("Trigger #{trigger.id} will be ignored for #{guest_name}")
|
@logger.debug("Trigger #{trigger.id} will be ignored for #{guest_name}")
|
||||||
triggers.delete_at(index)
|
triggers.delete_at(index)
|
||||||
|
@ -110,10 +143,10 @@ module Vagrant
|
||||||
@logger.debug("Running trigger #{trigger.id}...")
|
@logger.debug("Running trigger #{trigger.id}...")
|
||||||
|
|
||||||
if trigger.name
|
if trigger.name
|
||||||
@machine.ui.info(I18n.t("vagrant.trigger.fire_with_name",
|
@ui.info(I18n.t("vagrant.trigger.fire_with_name",
|
||||||
name: trigger.name))
|
name: trigger.name))
|
||||||
else
|
else
|
||||||
@machine.ui.info(I18n.t("vagrant.trigger.fire"))
|
@ui.info(I18n.t("vagrant.trigger.fire"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if trigger.info
|
if trigger.info
|
||||||
|
@ -146,14 +179,14 @@ module Vagrant
|
||||||
#
|
#
|
||||||
# @param [String] message The string to be printed
|
# @param [String] message The string to be printed
|
||||||
def info(message)
|
def info(message)
|
||||||
@machine.ui.info(message)
|
@ui.info(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Prints the given message at warn level for a trigger
|
# Prints the given message at warn level for a trigger
|
||||||
#
|
#
|
||||||
# @param [String] message The string to be printed
|
# @param [String] message The string to be printed
|
||||||
def warn(message)
|
def warn(message)
|
||||||
@machine.ui.warn(message)
|
@ui.warn(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Runs a script on a guest
|
# Runs a script on a guest
|
||||||
|
@ -167,14 +200,14 @@ module Vagrant
|
||||||
cmd = Shellwords.split(config.inline)
|
cmd = Shellwords.split(config.inline)
|
||||||
end
|
end
|
||||||
|
|
||||||
@machine.ui.detail(I18n.t("vagrant.trigger.run.inline", command: config.inline))
|
@ui.detail(I18n.t("vagrant.trigger.run.inline", command: config.inline))
|
||||||
else
|
else
|
||||||
cmd = File.expand_path(config.path, @env.root_path).shellescape
|
cmd = File.expand_path(config.path, @env.root_path).shellescape
|
||||||
args = Array(config.args)
|
args = Array(config.args)
|
||||||
cmd << " #{args.join(' ')}" if !args.empty?
|
cmd << " #{args.join(' ')}" if !args.empty?
|
||||||
cmd = Shellwords.split(cmd)
|
cmd = Shellwords.split(cmd)
|
||||||
|
|
||||||
@machine.ui.detail(I18n.t("vagrant.trigger.run.script", path: config.path))
|
@ui.detail(I18n.t("vagrant.trigger.run.script", path: config.path))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pick an execution method to run the script or inline string with
|
# Pick an execution method to run the script or inline string with
|
||||||
|
@ -199,22 +232,22 @@ module Vagrant
|
||||||
options[:color] = :red if !config.keep_color
|
options[:color] = :red if !config.keep_color
|
||||||
end
|
end
|
||||||
|
|
||||||
@machine.ui.detail(data, options)
|
@ui.detail(data, options)
|
||||||
end
|
end
|
||||||
if !exit_codes.include?(result.exit_code)
|
if !exit_codes.include?(result.exit_code)
|
||||||
raise Errors::TriggersBadExitCodes,
|
raise Errors::TriggersBadExitCodes,
|
||||||
code: result.exit_code
|
code: result.exit_code
|
||||||
end
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
@machine.ui.error(I18n.t("vagrant.errors.triggers_run_fail"))
|
@ui.error(I18n.t("vagrant.errors.triggers_run_fail"))
|
||||||
@machine.ui.error(e.message)
|
@ui.error(e.message)
|
||||||
|
|
||||||
if on_error == :halt
|
if on_error == :halt
|
||||||
@logger.debug("Trigger run encountered an error. Halting on error...")
|
@logger.debug("Trigger run encountered an error. Halting on error...")
|
||||||
raise e
|
raise e
|
||||||
else
|
else
|
||||||
@logger.debug("Trigger run encountered an error. Continuing on anyway...")
|
@logger.debug("Trigger run encountered an error. Continuing on anyway...")
|
||||||
@machine.ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
|
@ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -223,7 +256,16 @@ module Vagrant
|
||||||
#
|
#
|
||||||
# @param [ShellProvisioner/Config] config A Shell provisioner config
|
# @param [ShellProvisioner/Config] config A Shell provisioner config
|
||||||
def run_remote(config, on_error, exit_codes)
|
def run_remote(config, on_error, exit_codes)
|
||||||
unless @machine.state.id == :running
|
if !@machine
|
||||||
|
# machine doesn't even exist.
|
||||||
|
if on_error == :halt
|
||||||
|
raise Errors::TriggersGuestNotExist
|
||||||
|
else
|
||||||
|
@ui.warn(I18n.t("vagrant.errors.triggers_guest_not_exist"))
|
||||||
|
@ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
elsif @machine.state.id != :running
|
||||||
if on_error == :halt
|
if on_error == :halt
|
||||||
raise Errors::TriggersGuestNotRunning,
|
raise Errors::TriggersGuestNotRunning,
|
||||||
machine_name: @machine.name,
|
machine_name: @machine.name,
|
||||||
|
@ -258,7 +300,7 @@ module Vagrant
|
||||||
#
|
#
|
||||||
# @param [Integer] code Code to exit Vagrant on
|
# @param [Integer] code Code to exit Vagrant on
|
||||||
def trigger_abort(exit_code)
|
def trigger_abort(exit_code)
|
||||||
@machine.ui.warn(I18n.t("vagrant.trigger.abort"))
|
@ui.warn(I18n.t("vagrant.trigger.abort"))
|
||||||
exit(exit_code)
|
exit(exit_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -44,18 +44,22 @@ module VagrantPlugins
|
||||||
command.flatten!
|
command.flatten!
|
||||||
blk = block
|
blk = block
|
||||||
|
|
||||||
if !block_given? && command.last.is_a?(Hash)
|
if command.last.is_a?(Hash)
|
||||||
# We were given a hash rather than a block,
|
if block_given?
|
||||||
# so the last element should be the "config block"
|
extra_cfg = command.pop
|
||||||
# and the rest are commands for the trigger
|
else
|
||||||
blk = command.pop
|
# We were given a hash rather than a block,
|
||||||
|
# so the last element should be the "config block"
|
||||||
|
# and the rest are commands for the trigger
|
||||||
|
blk = command.pop
|
||||||
|
end
|
||||||
elsif !block_given?
|
elsif !block_given?
|
||||||
raise Vagrant::Errors::TriggersNoBlockGiven,
|
raise Vagrant::Errors::TriggersNoBlockGiven,
|
||||||
command: command
|
command: command
|
||||||
end
|
end
|
||||||
|
|
||||||
command.each do |cmd|
|
command.each do |cmd|
|
||||||
trigger = create_trigger(cmd, blk)
|
trigger = create_trigger(cmd, blk, extra_cfg)
|
||||||
@_before_triggers << trigger
|
@_before_triggers << trigger
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -69,18 +73,22 @@ module VagrantPlugins
|
||||||
command.flatten!
|
command.flatten!
|
||||||
blk = block
|
blk = block
|
||||||
|
|
||||||
if !block_given? && command.last.is_a?(Hash)
|
if command.last.is_a?(Hash)
|
||||||
# We were given a hash rather than a block,
|
if block_given?
|
||||||
# so the last element should be the "config block"
|
extra_cfg = command.pop
|
||||||
# and the rest are commands for the trigger
|
else
|
||||||
blk = command.pop
|
# We were given a hash rather than a block,
|
||||||
|
# so the last element should be the "config block"
|
||||||
|
# and the rest are commands for the trigger
|
||||||
|
blk = command.pop
|
||||||
|
end
|
||||||
elsif !block_given?
|
elsif !block_given?
|
||||||
raise Vagrant::Errors::TriggersNoBlockGiven,
|
raise Vagrant::Errors::TriggersNoBlockGiven,
|
||||||
command: command
|
command: command
|
||||||
end
|
end
|
||||||
|
|
||||||
command.each do |cmd|
|
command.each do |cmd|
|
||||||
trigger = create_trigger(cmd, blk)
|
trigger = create_trigger(cmd, blk, extra_cfg)
|
||||||
@_after_triggers << trigger
|
@_after_triggers << trigger
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -95,13 +103,15 @@ module VagrantPlugins
|
||||||
#
|
#
|
||||||
# @param [Symbol] command Vagrant command to create trigger on
|
# @param [Symbol] command Vagrant command to create trigger on
|
||||||
# @param [Block] block The defined config block
|
# @param [Block] block The defined config block
|
||||||
|
# @param [Hash] extra_cfg Extra configurations for a block defined trigger (Optional)
|
||||||
# @return [VagrantConfigTrigger]
|
# @return [VagrantConfigTrigger]
|
||||||
def create_trigger(command, block)
|
def create_trigger(command, block, extra_cfg=nil)
|
||||||
trigger = VagrantConfigTrigger.new(command)
|
trigger = VagrantConfigTrigger.new(command)
|
||||||
if block.is_a?(Hash)
|
if block.is_a?(Hash)
|
||||||
trigger.set_options(block)
|
trigger.set_options(block)
|
||||||
else
|
else
|
||||||
block.call(trigger, VagrantConfigTrigger)
|
block.call(trigger, VagrantConfigTrigger)
|
||||||
|
trigger.set_options(extra_cfg) if extra_cfg
|
||||||
end
|
end
|
||||||
return trigger
|
return trigger
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ module VagrantPlugins
|
||||||
# Defaults
|
# Defaults
|
||||||
DEFAULT_ON_ERROR = :halt
|
DEFAULT_ON_ERROR = :halt
|
||||||
DEFAULT_EXIT_CODE = 0
|
DEFAULT_EXIT_CODE = 0
|
||||||
|
VALID_TRIGGER_TYPES = [:command, :action, :hook].freeze
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
# Config class for a given Trigger
|
# Config class for a given Trigger
|
||||||
|
@ -89,6 +90,12 @@ module VagrantPlugins
|
||||||
# @return [Proc]
|
# @return [Proc]
|
||||||
attr_accessor :ruby
|
attr_accessor :ruby
|
||||||
|
|
||||||
|
# The type of trigger, which defines where it will fire. If not defined,
|
||||||
|
# the option will default to `:action`
|
||||||
|
#
|
||||||
|
# @return [Symbol]
|
||||||
|
attr_accessor :type
|
||||||
|
|
||||||
def initialize(command)
|
def initialize(command)
|
||||||
@logger = Log4r::Logger.new("vagrant::config::vm::trigger::config")
|
@logger = Log4r::Logger.new("vagrant::config::vm::trigger::config")
|
||||||
|
|
||||||
|
@ -103,6 +110,7 @@ module VagrantPlugins
|
||||||
@exit_codes = UNSET_VALUE
|
@exit_codes = UNSET_VALUE
|
||||||
@abort = UNSET_VALUE
|
@abort = UNSET_VALUE
|
||||||
@ruby = UNSET_VALUE
|
@ruby = UNSET_VALUE
|
||||||
|
@type = UNSET_VALUE
|
||||||
|
|
||||||
# Internal options
|
# Internal options
|
||||||
@id = SecureRandom.uuid
|
@id = SecureRandom.uuid
|
||||||
|
@ -135,6 +143,7 @@ module VagrantPlugins
|
||||||
@only_on = nil if @only_on == UNSET_VALUE
|
@only_on = nil if @only_on == UNSET_VALUE
|
||||||
@exit_codes = DEFAULT_EXIT_CODE if @exit_codes == UNSET_VALUE
|
@exit_codes = DEFAULT_EXIT_CODE if @exit_codes == UNSET_VALUE
|
||||||
@abort = nil if @abort == UNSET_VALUE
|
@abort = nil if @abort == UNSET_VALUE
|
||||||
|
@type = :action if @type == UNSET_VALUE
|
||||||
|
|
||||||
@ruby_block = nil if @ruby_block == UNSET_VALUE
|
@ruby_block = nil if @ruby_block == UNSET_VALUE
|
||||||
@ruby = nil if @ruby == UNSET_VALUE
|
@ruby = nil if @ruby == UNSET_VALUE
|
||||||
|
@ -187,20 +196,33 @@ module VagrantPlugins
|
||||||
if @abort == true
|
if @abort == true
|
||||||
@abort = 1
|
@abort = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if @type
|
||||||
|
@type = @type.to_sym
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [Array] array of strings of error messages from config option validation
|
# @return [Array] array of strings of error messages from config option validation
|
||||||
def validate(machine)
|
def validate(machine)
|
||||||
errors = _detected_errors
|
errors = _detected_errors
|
||||||
|
|
||||||
commands = []
|
if @type && !VALID_TRIGGER_TYPES.include?(@type)
|
||||||
Vagrant.plugin("2").manager.commands.each do |key,data|
|
errors << I18n.t("vagrant.config.triggers.bad_trigger_type",
|
||||||
commands.push(key)
|
type: @type,
|
||||||
|
trigger: @command,
|
||||||
|
types: VALID_TRIGGER_TYPES.join(', '))
|
||||||
end
|
end
|
||||||
|
|
||||||
if !commands.include?(@command) && @command != :all
|
if @type == :command || !@type
|
||||||
machine.ui.warn(I18n.t("vagrant.config.triggers.bad_command_warning",
|
commands = []
|
||||||
cmd: @command))
|
Vagrant.plugin("2").manager.commands.each do |key,data|
|
||||||
|
commands.push(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
if !commands.include?(@command) && @command != :all
|
||||||
|
machine.ui.warn(I18n.t("vagrant.config.triggers.bad_command_warning",
|
||||||
|
cmd: @command))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if @run
|
if @run
|
||||||
|
|
|
@ -296,7 +296,7 @@ en:
|
||||||
abort: |-
|
abort: |-
|
||||||
Vagrant has been configured to abort. Terminating now...
|
Vagrant has been configured to abort. Terminating now...
|
||||||
start: |-
|
start: |-
|
||||||
Running triggers %{stage} %{action} ...
|
Running %{type} triggers %{stage} %{action} ...
|
||||||
fire_with_name: |-
|
fire_with_name: |-
|
||||||
Running trigger: %{name}...
|
Running trigger: %{name}...
|
||||||
fire: |-
|
fire: |-
|
||||||
|
@ -1502,6 +1502,8 @@ en:
|
||||||
Trigger run failed
|
Trigger run failed
|
||||||
triggers_guest_not_running: |-
|
triggers_guest_not_running: |-
|
||||||
Could not run remote script on %{machine_name} because its state is %{state}
|
Could not run remote script on %{machine_name} because its state is %{state}
|
||||||
|
triggers_guest_not_exist: |-
|
||||||
|
Could not run remote script on guest because it does not exist.
|
||||||
triggers_bad_exit_codes: |-
|
triggers_bad_exit_codes: |-
|
||||||
A script exited with an unacceptable exit code %{code}.
|
A script exited with an unacceptable exit code %{code}.
|
||||||
triggers_no_block_given: |-
|
triggers_no_block_given: |-
|
||||||
|
@ -1826,6 +1828,8 @@ en:
|
||||||
values are exactly the same, only the name of the option has changed.
|
values are exactly the same, only the name of the option has changed.
|
||||||
ssh_config_missing: "`config` file must exist: %{path}"
|
ssh_config_missing: "`config` file must exist: %{path}"
|
||||||
triggers:
|
triggers:
|
||||||
|
bad_trigger_type: |-
|
||||||
|
The type '%{type}' defined for trigger '%{trigger}' is not valid. Must be one of the following types: '%{types}'
|
||||||
bad_command_warning: |-
|
bad_command_warning: |-
|
||||||
The command '%{cmd}' was not found for this trigger.
|
The command '%{cmd}' was not found for this trigger.
|
||||||
name_bad_type: |-
|
name_bad_type: |-
|
||||||
|
|
|
@ -71,6 +71,7 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigTrigger do
|
||||||
cfg.only_on = :guest
|
cfg.only_on = :guest
|
||||||
cfg.ignore = "up"
|
cfg.ignore = "up"
|
||||||
cfg.abort = true
|
cfg.abort = true
|
||||||
|
cfg.type = "action"
|
||||||
cfg.ruby do
|
cfg.ruby do
|
||||||
var = 1+1
|
var = 1+1
|
||||||
end
|
end
|
||||||
|
@ -112,6 +113,11 @@ describe VagrantPlugins::Kernel_V2::VagrantConfigTrigger do
|
||||||
|
|
||||||
expect(cfg.abort).to eq(1)
|
expect(cfg.abort).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "converts types to symbols" do
|
||||||
|
cfg.finalize!
|
||||||
|
expect(cfg.type).to eq(:action)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "defining a basic trigger config" do
|
describe "defining a basic trigger config" do
|
||||||
|
|
|
@ -22,6 +22,10 @@ describe Vagrant::Action::Builder do
|
||||||
@app = app
|
@app = app
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
define_method(:call) do |env|
|
define_method(:call) do |env|
|
||||||
env[:data] << "#{data}_in"
|
env[:data] << "#{data}_in"
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
|
@ -147,6 +151,10 @@ describe Vagrant::Action::Builder do
|
||||||
@app = app
|
@app = app
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
define_method(:call) do |env|
|
define_method(:call) do |env|
|
||||||
env[:data] << "#{letter}1"
|
env[:data] << "#{letter}1"
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
|
@ -266,6 +274,10 @@ describe Vagrant::Action::Builder do
|
||||||
@app = app
|
@app = app
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
define_method(:call) do |env|
|
define_method(:call) do |env|
|
||||||
inner = described_klass.new
|
inner = described_klass.new
|
||||||
inner.use wrapper_proc[2]
|
inner.use wrapper_proc[2]
|
||||||
|
|
|
@ -10,6 +10,10 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
@app = app
|
@app = app
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
define_method(:call) do |env|
|
define_method(:call) do |env|
|
||||||
env[:data] << "#{data}_in"
|
env[:data] << "#{data}_in"
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
|
@ -103,6 +107,10 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
env[:arg] = arg
|
env[:arg] = arg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
def call(env); end
|
def call(env); end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -126,6 +134,10 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
@env = env
|
@env = env
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
end
|
end
|
||||||
|
@ -137,6 +149,10 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
def recover(env)
|
def recover(env)
|
||||||
env[:steps] << :recover_A
|
env[:steps] << :recover_A
|
||||||
end
|
end
|
||||||
|
@ -148,6 +164,10 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
def recover(env)
|
def recover(env)
|
||||||
env[:steps] << :recover_B
|
env[:steps] << :recover_B
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require File.expand_path("../../../base", __FILE__)
|
require File.expand_path("../../../base", __FILE__)
|
||||||
|
|
||||||
describe Vagrant::Action::Runner do
|
describe Vagrant::Action::Runner do
|
||||||
let(:instance) { described_class.new }
|
let(:instance) { described_class.new(action_name: "test") }
|
||||||
|
|
||||||
it "should raise an error if an invalid callable is given" do
|
it "should raise an error if an invalid callable is given" do
|
||||||
expect { instance.run(7) }.to raise_error(ArgumentError, /must be a callable/)
|
expect { instance.run(7) }.to raise_error(ArgumentError, /must be a callable/)
|
||||||
|
@ -18,11 +18,29 @@ describe Vagrant::Action::Runner do
|
||||||
raise Exception, "BANG"
|
raise Exception, "BANG"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
callable = klass.new.method(:action)
|
callable = klass.new.method(:action)
|
||||||
expect { instance.run(callable) }.to raise_error(Exception, "BANG")
|
expect { instance.run(callable) }.to raise_error(Exception, "BANG")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should be able to use a Class as a callable" do
|
it "should be able to use a Class as a callable" do
|
||||||
|
callable = Class.new do
|
||||||
|
def initialize(app, env)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.name
|
||||||
|
"TestAction"
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
raise Exception, "BOOM"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { instance.run(callable) }.to raise_error(Exception, "BOOM")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to use a Class as a callable with no name attribute" do
|
||||||
callable = Class.new do
|
callable = Class.new do
|
||||||
def initialize(app, env)
|
def initialize(app, env)
|
||||||
end
|
end
|
||||||
|
@ -63,7 +81,7 @@ describe Vagrant::Action::Runner do
|
||||||
result = env["data"]
|
result = env["data"]
|
||||||
end
|
end
|
||||||
|
|
||||||
instance = described_class.new("data" => "bar")
|
instance = described_class.new("data" => "bar", action_name: "test")
|
||||||
instance.run(callable)
|
instance.run(callable)
|
||||||
expect(result).to eq("bar")
|
expect(result).to eq("bar")
|
||||||
end
|
end
|
||||||
|
@ -74,7 +92,7 @@ describe Vagrant::Action::Runner do
|
||||||
result = env["data"]
|
result = env["data"]
|
||||||
end
|
end
|
||||||
|
|
||||||
instance = described_class.new { { "data" => "bar" } }
|
instance = described_class.new { { "data" => "bar", action_name: "test" } }
|
||||||
instance.run(callable)
|
instance.run(callable)
|
||||||
expect(result).to eq("bar")
|
expect(result).to eq("bar")
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,6 +28,8 @@ describe Vagrant::CLI do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#execute" do
|
describe "#execute" do
|
||||||
|
let(:triggers) { double("triggers") }
|
||||||
|
|
||||||
it "invokes help and exits with 1 if invalid command" do
|
it "invokes help and exits with 1 if invalid command" do
|
||||||
subject = described_class.new(["i-dont-exist"], env)
|
subject = described_class.new(["i-dont-exist"], env)
|
||||||
expect(subject).to receive(:help).once
|
expect(subject).to receive(:help).once
|
||||||
|
@ -54,6 +56,37 @@ describe Vagrant::CLI do
|
||||||
expect(checkpoint).to receive(:display)
|
expect(checkpoint).to receive(:display)
|
||||||
described_class.new(["destroy"], env).execute
|
described_class.new(["destroy"], env).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "fires triggers, if enabled" do
|
||||||
|
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).
|
||||||
|
with("typed_triggers").and_return("true")
|
||||||
|
allow(triggers).to receive(:fire_triggers)
|
||||||
|
|
||||||
|
commands[:destroy] = [command_lambda("destroy", 42), {}]
|
||||||
|
|
||||||
|
allow(Vagrant::Plugin::V2::Trigger).to receive(:new).and_return(triggers)
|
||||||
|
|
||||||
|
subject = described_class.new(["destroy"], env)
|
||||||
|
|
||||||
|
expect(triggers).to receive(:fire_triggers).twice
|
||||||
|
|
||||||
|
expect(subject).not_to receive(:help)
|
||||||
|
expect(subject.execute).to eql(42)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not fire triggers if disabled" do
|
||||||
|
allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).
|
||||||
|
with("typed_triggers").and_return("false")
|
||||||
|
|
||||||
|
commands[:destroy] = [command_lambda("destroy", 42), {}]
|
||||||
|
|
||||||
|
subject = described_class.new(["destroy"], env)
|
||||||
|
|
||||||
|
expect(triggers).not_to receive(:fire_triggers)
|
||||||
|
|
||||||
|
expect(subject).not_to receive(:help)
|
||||||
|
expect(subject.execute).to eql(42)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#help" do
|
describe "#help" do
|
||||||
|
|
|
@ -411,38 +411,6 @@ describe Vagrant::Machine do
|
||||||
expect(subject.ui).to_not have_received(:warn)
|
expect(subject.ui).to_not have_received(:warn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with the vagrant-triggers community plugin" do
|
|
||||||
it "should not call the internal trigger functions if installed" do
|
|
||||||
action_name = :destroy
|
|
||||||
callable = lambda { |_env| }
|
|
||||||
|
|
||||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
|
||||||
|
|
||||||
# The first call here is to allow the environment to setup with attempting
|
|
||||||
# to load a plugin that does not exist
|
|
||||||
expect(Vagrant::Plugin::Manager.instance).to receive(:installed_plugins)
|
|
||||||
.and_return({})
|
|
||||||
|
|
||||||
expect(Vagrant::Plugin::Manager.instance).to receive(:installed_plugins)
|
|
||||||
.and_return({"vagrant-triggers"=>"stuff"})
|
|
||||||
|
|
||||||
expect(instance.instance_variable_get(:@triggers)).not_to receive(:fire_triggers)
|
|
||||||
instance.action(action_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should call the internal trigger functions if not installed" do
|
|
||||||
action_name = :destroy
|
|
||||||
callable = lambda { |_env| }
|
|
||||||
|
|
||||||
allow(provider).to receive(:action).with(action_name).and_return(callable)
|
|
||||||
allow(Vagrant::Plugin::Manager.instance).to receive(:installed_plugins)
|
|
||||||
.and_return({})
|
|
||||||
|
|
||||||
expect(instance.instance_variable_get(:@triggers)).to receive(:fire_triggers).twice
|
|
||||||
instance.action(action_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#action_raw" do
|
describe "#action_raw" do
|
||||||
|
|
|
@ -17,9 +17,10 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
allow(m).to receive(:state).and_return(state)
|
allow(m).to receive(:state).and_return(state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
let(:ui) { Vagrant::UI::Silent.new }
|
||||||
let(:env) { {
|
let(:env) { {
|
||||||
machine: machine,
|
machine: machine,
|
||||||
ui: Vagrant::UI::Silent.new,
|
ui: ui,
|
||||||
} }
|
} }
|
||||||
|
|
||||||
let(:triggers) { VagrantPlugins::Kernel_V2::TriggerConfig.new }
|
let(:triggers) { VagrantPlugins::Kernel_V2::TriggerConfig.new }
|
||||||
|
@ -36,19 +37,33 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
let(:subject) { described_class.new(env, triggers, machine) }
|
let(:subject) { described_class.new(env, triggers, machine, ui) }
|
||||||
|
|
||||||
context "#fire_triggers" do
|
context "#fire_triggers" do
|
||||||
it "raises an error if an inproper stage is given" do
|
it "raises an error if an inproper stage is given" do
|
||||||
expect{ subject.fire_triggers(:up, :not_real, "guest") }.
|
expect{ subject.fire_triggers(:up, :not_real, "guest", :action) }.
|
||||||
to raise_error(Vagrant::Errors::TriggersNoStageGiven)
|
to raise_error(Vagrant::Errors::TriggersNoStageGiven)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "does not fire triggers if community plugin is detected" do
|
||||||
|
allow(subject).to receive(:community_plugin_detected?).and_return(true)
|
||||||
|
|
||||||
|
expect(subject).not_to receive(:fire)
|
||||||
|
subject.fire_triggers(:up, :before, "guest", :action)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does fire triggers if community plugin is not detected" do
|
||||||
|
allow(subject).to receive(:community_plugin_detected?).and_return(false)
|
||||||
|
|
||||||
|
expect(subject).to receive(:fire)
|
||||||
|
subject.fire_triggers(:up, :before, "guest", :action)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#filter_triggers" do
|
context "#filter_triggers" do
|
||||||
it "returns all triggers if no constraints" do
|
it "returns all triggers if no constraints" do
|
||||||
before_triggers = triggers.before_triggers
|
before_triggers = triggers.before_triggers
|
||||||
filtered_triggers = subject.send(:filter_triggers, before_triggers, "guest")
|
filtered_triggers = subject.send(:filter_triggers, before_triggers, "guest", :action)
|
||||||
expect(filtered_triggers).to eq(before_triggers)
|
expect(filtered_triggers).to eq(before_triggers)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -59,7 +74,7 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
|
|
||||||
after_triggers = triggers.after_triggers
|
after_triggers = triggers.after_triggers
|
||||||
expect(after_triggers.size).to eq(3)
|
expect(after_triggers.size).to eq(3)
|
||||||
subject.send(:filter_triggers, after_triggers, "ubuntu")
|
subject.send(:filter_triggers, after_triggers, "ubuntu", :action)
|
||||||
expect(after_triggers.size).to eq(2)
|
expect(after_triggers.size).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +85,7 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
|
|
||||||
after_triggers = triggers.after_triggers
|
after_triggers = triggers.after_triggers
|
||||||
expect(after_triggers.size).to eq(3)
|
expect(after_triggers.size).to eq(3)
|
||||||
subject.send(:filter_triggers, after_triggers, "ubuntu-guest")
|
subject.send(:filter_triggers, after_triggers, "ubuntu-guest", :action)
|
||||||
expect(after_triggers.size).to eq(3)
|
expect(after_triggers.size).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -81,7 +96,7 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
|
|
||||||
after_triggers = triggers.after_triggers
|
after_triggers = triggers.after_triggers
|
||||||
expect(after_triggers.size).to eq(3)
|
expect(after_triggers.size).to eq(3)
|
||||||
subject.send(:filter_triggers, after_triggers, "ubuntu-guest")
|
subject.send(:filter_triggers, after_triggers, "ubuntu-guest", :action)
|
||||||
expect(after_triggers.size).to eq(3)
|
expect(after_triggers.size).to eq(3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -101,7 +116,7 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
|
|
||||||
it "prints messages at INFO" do
|
it "prints messages at INFO" do
|
||||||
output = ""
|
output = ""
|
||||||
allow(machine.ui).to receive(:info) do |data|
|
allow(ui).to receive(:info) do |data|
|
||||||
output << data
|
output << data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -115,7 +130,7 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
|
|
||||||
it "prints messages at WARN" do
|
it "prints messages at WARN" do
|
||||||
output = ""
|
output = ""
|
||||||
allow(machine.ui).to receive(:warn) do |data|
|
allow(ui).to receive(:warn) do |data|
|
||||||
output << data
|
output << data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -304,6 +319,29 @@ describe Vagrant::Plugin::V2::Trigger do
|
||||||
trigger_run.finalize!
|
trigger_run.finalize!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with no machine existing" do
|
||||||
|
let(:machine) { nil }
|
||||||
|
|
||||||
|
it "raises an error and halts if guest does not exist" do
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
exit_codes = trigger.exit_codes
|
||||||
|
|
||||||
|
expect { subject.send(:run_remote, shell_config, on_error, exit_codes) }.
|
||||||
|
to raise_error(Vagrant::Errors::TriggersGuestNotExist)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "continues on if guest does not exist but is configured to continue on error" do
|
||||||
|
trigger = trigger_run.before_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
exit_codes = trigger.exit_codes
|
||||||
|
|
||||||
|
subject.send(:run_remote, shell_config, on_error, exit_codes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "raises an error and halts if guest is not running" do
|
it "raises an error and halts if guest is not running" do
|
||||||
allow(machine.state).to receive(:id).and_return(:not_running)
|
allow(machine.state).to receive(:id).and_return(:not_running)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Vagrant Experimental Feature Flag"
|
||||||
|
sidebar_current: "experimental"
|
||||||
|
description: |-
|
||||||
|
Introduction to Vagrants Experimental Feature Flag
|
||||||
|
---
|
||||||
|
|
||||||
|
# Experimental Feature Flag
|
||||||
|
|
||||||
|
Some features that aren't ready for release can be enabled through this feature
|
||||||
|
flag. There are a couple of different ways of going about enabling these features.
|
||||||
|
It is also worth noting that Vagrant will not validate the existance of a feature
|
||||||
|
flag.
|
||||||
|
|
||||||
|
For example if you are on Linux or Mac, and you wish to enable every single experimental feature, you can set the flag
|
||||||
|
to "on" by setting it to `1`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export VAGRANT_EXPERIMENTAL="1"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also enable some or many features if there are specific ones you would like,
|
||||||
|
but don't want every single feature enabled:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Only enables feature_one
|
||||||
|
export VAGRANT_EXPERIMENTAL="feature_one"
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Enables both feature_one and feature_two
|
||||||
|
export VAGRANT_EXPERIMENTAL="feature_one,feature_two"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Valid experimental features
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Advanced topic!</strong> This is an advanced topic for use only if
|
||||||
|
you want to use new Vagrant features. If you are just getting
|
||||||
|
started with Vagrant, you may safely skip this section.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
This is a list of all the valid experimental features that Vagrant recognizes:
|
||||||
|
|
||||||
|
### `typed_triggers`
|
||||||
|
|
||||||
|
Enabling this feature allows triggers to recognize and execute `:type` triggers.
|
||||||
|
More information about how these should be used can be found on the [trigger documentation page](/docs/triggers/configuration.html#trigger-types)
|
|
@ -10,7 +10,28 @@ description: |-
|
||||||
|
|
||||||
Vagrant Triggers has a few options to define trigger behavior.
|
Vagrant Triggers has a few options to define trigger behavior.
|
||||||
|
|
||||||
## Options
|
## Execution Order
|
||||||
|
|
||||||
|
The trigger config block takes two different operations that determine when a trigger
|
||||||
|
should fire:
|
||||||
|
|
||||||
|
* `before`
|
||||||
|
* `after`
|
||||||
|
|
||||||
|
These define _how_ the trigger behaves and when it should fire off during
|
||||||
|
the Vagrant life cycle. A simple example of a _before_ operation could look like:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.before :up do |t|
|
||||||
|
t.info = "Bringing up your Vagrant guest machine!"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Triggers can also be used with [_commands_](#commands), [_actions_](#actions), or [_hooks_](#hooks).
|
||||||
|
By default triggers will be defined to run before or after a Vagrant guest. For more
|
||||||
|
detailed examples of how to use triggers, check out the [usage section](/docs/triggers/usage.html).
|
||||||
|
|
||||||
|
## Trigger Options
|
||||||
|
|
||||||
The trigger class takes various options.
|
The trigger class takes various options.
|
||||||
|
|
||||||
|
@ -58,3 +79,117 @@ The trigger class takes various options.
|
||||||
* `exit_codes` (integer, array) - A set of acceptable exit codes to continue on. Defaults to `0` if option is absent. For now only valid with the `run` option.
|
* `exit_codes` (integer, array) - A set of acceptable exit codes to continue on. Defaults to `0` if option is absent. For now only valid with the `run` option.
|
||||||
|
|
||||||
* `abort` (integer,boolean) - An option that will exit the running Vagrant process once the trigger fires. If set to `true`, Vagrant will use exit code 1. Otherwise, an integer can be provided and Vagrant will it as its exit code when aborting.
|
* `abort` (integer,boolean) - An option that will exit the running Vagrant process once the trigger fires. If set to `true`, Vagrant will use exit code 1. Otherwise, an integer can be provided and Vagrant will it as its exit code when aborting.
|
||||||
|
|
||||||
|
## Trigger Types
|
||||||
|
|
||||||
|
Optionally, it is possible to define a trigger that executes around Vagrant commands,
|
||||||
|
hooks, and actions.
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Warning!</strong> This feature is still experimental and may break or
|
||||||
|
change in between releases. Use at your own risk.
|
||||||
|
|
||||||
|
This feature currently reqiures the experimental flag to be used. To explicitly enable this feature, you can set the experimental flag to:
|
||||||
|
|
||||||
|
```
|
||||||
|
VAGRANT_EXPERIMENTAL="typed_triggers"
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that `VAGRANT_EXPERIMENTAL` is an environment variable. For more
|
||||||
|
information about this flag visit the [Experimental docs page](/docs/experimental/)
|
||||||
|
for more info. Without this flag enabled, triggers with the `:type` option
|
||||||
|
will be ignored.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
A trigger can be one of three types:
|
||||||
|
|
||||||
|
* `type` (symbol) - Optional
|
||||||
|
- `:action` - Action triggers run before or after a Vagrant action
|
||||||
|
- `:command` - Command triggers run before or after a Vagrant command
|
||||||
|
- `:hook` - Action hook triggers run before or after a Vagrant hook
|
||||||
|
|
||||||
|
These types determine when and where a defined trigger will execute.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.after :destroy, type: :command do |t|
|
||||||
|
t.warn = "Destroy command completed"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Quick Note
|
||||||
|
|
||||||
|
Triggers _without_ the type option will run before or after a Vagrant guest.
|
||||||
|
|
||||||
|
Older Vagrant versions will unfortunetly not be able to properly parse the new
|
||||||
|
`:type` option. If you are worried about older clients failing to parse your Vagrantfile,
|
||||||
|
you can guard the new trigger based on the version of Vagrant:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
if Vagrant.version?(">= 2.3.0")
|
||||||
|
config.trigger.before :status, type: :command do |t|
|
||||||
|
t.info = "before action!!!!!!!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
Command typed triggers can be defined for any valid Vagrant command. They will always
|
||||||
|
run before or after the command.
|
||||||
|
|
||||||
|
The difference between this and the default behavior is that these triggers are
|
||||||
|
not attached to any specific guest, and will always run before or after the given
|
||||||
|
command. A simple example might be running a trigger before the up command to give
|
||||||
|
a simple message to the user:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.before :up, type: :command do |t|
|
||||||
|
t.info = "Before command!"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
For a more detailed example, please check out the [examples](/docs/triggers/usage.html#commands)
|
||||||
|
page for more.
|
||||||
|
|
||||||
|
### Hooks
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Advanced topic!</strong> This is an advanced topic for use only if
|
||||||
|
you want to execute triggers around Vagrant hooks. If you are just getting
|
||||||
|
started with Vagrant and triggers, you may safely skip this section.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Hook typed triggers can be defined for any valid Vagrant action hook that is defined.
|
||||||
|
|
||||||
|
A simple example would be running a trigger on a given hook called `action_hook_name`.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.after :action_hook_name, type: :hook do |t|
|
||||||
|
t.info = "After action hook!"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
For a more detailed example, please check out the [examples](/docs/triggers/usage.html#hooks)
|
||||||
|
page for more.
|
||||||
|
|
||||||
|
### Actions
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Advanced topic!</strong> This is an advanced topic for use only if
|
||||||
|
you want to execute triggers around Vagrant actions. If you are just getting
|
||||||
|
started with Vagrant and triggers, you may safely skip this section.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Action typed triggers can be defined for any valid Vagrant action class. Actions
|
||||||
|
in this case refer to the Vagrant class `#Action`, which is used internally to
|
||||||
|
Vagrant and in every Vagrant plugin.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.before :"Action::Class::Name", type: :action do |t|
|
||||||
|
t.info = "Before action class!
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
For a more detailed example, please check out the [examples](/docs/triggers/usage.html#actions)
|
||||||
|
page for more.
|
||||||
|
|
|
@ -131,3 +131,70 @@ Vagrant.configure("2") do |config|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Typed Triggers
|
||||||
|
|
||||||
|
Below are some basic examples of using `:type` triggers. They cover commands, hooks,
|
||||||
|
and actions.
|
||||||
|
|
||||||
|
It is important to note that while `command` triggers will be a fairly common use case,
|
||||||
|
both `action` and `hook` triggers are more complicated and are a more advanced use case.
|
||||||
|
|
||||||
|
#### Commands
|
||||||
|
|
||||||
|
The most common use case for typed triggers are with `command`. These kinds of
|
||||||
|
triggers allow you to run something before or after a subcommand in Vagrant.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.after :status, type: :command do |t|
|
||||||
|
t.info = "Showing status of all VMs!"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Because they are specifically for subcommands, they do not work with any guest
|
||||||
|
operations like `run_remote` or if you define the trigger as a guest trigger.
|
||||||
|
|
||||||
|
#### Hooks
|
||||||
|
|
||||||
|
Below is an example of a Vagrant trigger that runs before and after each defined
|
||||||
|
provisioner:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.before :provisioner_run, type: :hook do |t|
|
||||||
|
t.info = "Before the provision!"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provision "file", source: "scripts/script.sh", destination: "/test/script.sh"
|
||||||
|
|
||||||
|
config.vm.provision "shell", inline: <<-SHELL
|
||||||
|
echo "Provision the guest!"
|
||||||
|
SHELL
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice how this trigger runs before _each_ provisioner defined for the guest:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
==> guest: Running provisioner: Sandbox (file)...
|
||||||
|
==> vargant: Running hook triggers before provisioner_run ...
|
||||||
|
==> vargant: Running trigger...
|
||||||
|
==> vargant: Before the provision!
|
||||||
|
guest: /home/hashicorp/vagrant-sandbox/scripts/script.sh => /home/vagrant/test/script.sh
|
||||||
|
==> guest: Running provisioner: shell...
|
||||||
|
==> vargant: Running hook triggers before provisioner_run ...
|
||||||
|
==> vargant: Running trigger...
|
||||||
|
==> vargant: Before the provision!
|
||||||
|
guest: Running: inline script
|
||||||
|
guest: Provision the guest!
|
||||||
|
```
|
||||||
|
#### Actions
|
||||||
|
|
||||||
|
With action typed triggers, you can fire off triggers before or after certain
|
||||||
|
Action classes. A simple example of this might be warning the user when Vagrant
|
||||||
|
invokes the `GracefulHalt` action.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.before :"Vagrant::Action::Builtin::GracefulHalt", type: :action do |t|
|
||||||
|
t.warn = "Vagrant is halting your guest..."
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
|
@ -216,6 +216,10 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("experimental") %>>
|
||||||
|
<a href="/docs/experimental/">Experimental</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("other") %>>
|
<li<%= sidebar_current("other") %>>
|
||||||
<a href="/docs/other/">Other</a>
|
<a href="/docs/other/">Other</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
|
Loading…
Reference in New Issue