Merge pull request #9713 from briancain/vagrant-triggers-config
Integrate vagrant-triggers plugin functionality into core Vagrant
This commit is contained in:
commit
5643ba0c7d
|
@ -776,6 +776,18 @@ module Vagrant
|
||||||
error_key(:synced_folder_unusable)
|
error_key(:synced_folder_unusable)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class TriggersGuestNotRunning < VagrantError
|
||||||
|
error_key(:triggers_guest_not_running)
|
||||||
|
end
|
||||||
|
|
||||||
|
class TriggersNoBlockGiven < VagrantError
|
||||||
|
error_key(:triggers_no_block_given)
|
||||||
|
end
|
||||||
|
|
||||||
|
class TriggersNoStageGiven < VagrantError
|
||||||
|
error_key(:triggers_no_stage_given)
|
||||||
|
end
|
||||||
|
|
||||||
class UIExpectsTTY < VagrantError
|
class UIExpectsTTY < VagrantError
|
||||||
error_key(:ui_expects_tty)
|
error_key(:ui_expects_tty)
|
||||||
end
|
end
|
||||||
|
|
|
@ -149,6 +149,8 @@ module Vagrant
|
||||||
# Output a bunch of information about this machine in
|
# Output a bunch of information about this machine in
|
||||||
# machine-readable format in case someone is listening.
|
# machine-readable format in case someone is listening.
|
||||||
@ui.machine("metadata", "provider", provider_name)
|
@ui.machine("metadata", "provider", provider_name)
|
||||||
|
|
||||||
|
@triggers = Vagrant::Plugin::V2::Trigger.new(@env, @config.trigger, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This calls an action on the provider. The provider may or may not
|
# This calls an action on the provider. The provider may or may not
|
||||||
|
@ -159,6 +161,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)
|
||||||
|
@triggers.fire_triggers(name, :before, @name.to_s)
|
||||||
@logger.info("Calling action: #{name} on provider #{@provider}")
|
@logger.info("Calling action: #{name} on provider #{@provider}")
|
||||||
|
|
||||||
opts ||= {}
|
opts ||= {}
|
||||||
|
@ -185,7 +188,7 @@ module Vagrant
|
||||||
locker = @env.method(:lock) if lock && !name.to_s.start_with?("ssh")
|
locker = @env.method(:lock) if lock && !name.to_s.start_with?("ssh")
|
||||||
|
|
||||||
# Lock this machine for the duration of this action
|
# Lock this machine for the duration of this action
|
||||||
locker.call("machine-action-#{id}") do
|
return_env = locker.call("machine-action-#{id}") do
|
||||||
# Get the callable from the provider.
|
# Get the callable from the provider.
|
||||||
callable = @provider.action(name)
|
callable = @provider.action(name)
|
||||||
|
|
||||||
|
@ -203,6 +206,10 @@ module Vagrant
|
||||||
ui.machine("action", name.to_s, "end")
|
ui.machine("action", name.to_s, "end")
|
||||||
action_result
|
action_result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@triggers.fire_triggers(name, :after, @name.to_s)
|
||||||
|
# preserve returning environment after machine action runs
|
||||||
|
return return_env
|
||||||
rescue Errors::EnvironmentLockedError
|
rescue Errors::EnvironmentLockedError
|
||||||
raise Errors::MachineActionLockedError,
|
raise Errors::MachineActionLockedError,
|
||||||
action: name,
|
action: name,
|
||||||
|
|
|
@ -19,6 +19,7 @@ module Vagrant
|
||||||
autoload :Push, "vagrant/plugin/v2/push"
|
autoload :Push, "vagrant/plugin/v2/push"
|
||||||
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
|
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
|
||||||
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
|
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
|
||||||
|
autoload :Trigger, "vagrant/plugin/v2/trigger"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
require 'fileutils'
|
||||||
|
require 'log4r'
|
||||||
|
require 'shellwords'
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/shell/provisioner")
|
||||||
|
require "vagrant/util/subprocess"
|
||||||
|
require "vagrant/util/platform"
|
||||||
|
require "vagrant/util/powershell"
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
module Plugin
|
||||||
|
module V2
|
||||||
|
class Trigger
|
||||||
|
# @return [Kernel_V2::Config::Trigger]
|
||||||
|
attr_reader :config
|
||||||
|
|
||||||
|
# This class is responsible for setting up basic triggers that were
|
||||||
|
# defined inside a Vagrantfile.
|
||||||
|
#
|
||||||
|
# @param [Vagrant::Environment] env Vagrant environment
|
||||||
|
# @param [Kernel_V2::TriggerConfig] config Trigger configuration
|
||||||
|
# @param [Vagrant::Machine] machine Active Machine
|
||||||
|
def initialize(env, config, machine)
|
||||||
|
@env = env
|
||||||
|
@config = config
|
||||||
|
@machine = machine
|
||||||
|
|
||||||
|
@logger = Log4r::Logger.new("vagrant::trigger::#{self.class.to_s.downcase}")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fires all triggers, if any are defined for the action and guest
|
||||||
|
#
|
||||||
|
# @param [Symbol] action Vagrant command to fire trigger on
|
||||||
|
# @param [Symbol] stage :before or :after
|
||||||
|
# @param [String] guest_name The guest that invoked firing the triggers
|
||||||
|
def fire_triggers(action, stage, guest_name)
|
||||||
|
# get all triggers matching action
|
||||||
|
triggers = []
|
||||||
|
if stage == :before
|
||||||
|
triggers = config.before_triggers.select do |t|
|
||||||
|
t.command == action || (t.command == :all && !t.ignore.include?(action))
|
||||||
|
end
|
||||||
|
elsif stage == :after
|
||||||
|
triggers = config.after_triggers.select do |t|
|
||||||
|
t.command == action || (t.command == :all && !t.ignore.include?(action))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise Errors::TriggersNoStageGiven,
|
||||||
|
action: action,
|
||||||
|
stage: stage,
|
||||||
|
guest_name: guest_name
|
||||||
|
end
|
||||||
|
|
||||||
|
triggers = filter_triggers(triggers, guest_name)
|
||||||
|
|
||||||
|
if !triggers.empty?
|
||||||
|
@logger.info("Firing trigger for action #{action} on guest #{guest_name}")
|
||||||
|
@machine.ui.info(I18n.t("vagrant.trigger.start", stage: stage, action: action))
|
||||||
|
fire(triggers, guest_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# Internal methods, don't call these.
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Filters triggers to be fired based on configured restraints
|
||||||
|
#
|
||||||
|
# @param [Array] triggers An array of triggers to be filtered
|
||||||
|
# @param [String] guest_name The name of the current guest
|
||||||
|
# @return [Array] The filtered array of triggers
|
||||||
|
def filter_triggers(triggers, guest_name)
|
||||||
|
# look for only_on trigger constraint and if it doesn't match guest
|
||||||
|
# name, throw it away also be sure to preserve order
|
||||||
|
filter = triggers.dup
|
||||||
|
|
||||||
|
filter.each do |trigger|
|
||||||
|
index = nil
|
||||||
|
match = false
|
||||||
|
if trigger.only_on
|
||||||
|
trigger.only_on.each do |o|
|
||||||
|
if o.match(guest_name)
|
||||||
|
# trigger matches on current guest, so we're fine to use it
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# no matches found, so don't use trigger for guest
|
||||||
|
index = triggers.index(trigger) unless match == true
|
||||||
|
end
|
||||||
|
|
||||||
|
if index
|
||||||
|
@logger.debug("Trigger #{trigger.id} will be ignored for #{guest_name}")
|
||||||
|
triggers.delete_at(index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return triggers
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fires off all triggers in the given array
|
||||||
|
#
|
||||||
|
# @param [Array] triggers An array of triggers to be fired
|
||||||
|
def fire(triggers, guest_name)
|
||||||
|
# ensure on_error is respected by exiting or continuing
|
||||||
|
|
||||||
|
triggers.each do |trigger|
|
||||||
|
@logger.debug("Running trigger #{trigger.id}...")
|
||||||
|
|
||||||
|
if trigger.name
|
||||||
|
@machine.ui.info(I18n.t("vagrant.trigger.fire_with_name",
|
||||||
|
name: trigger.name))
|
||||||
|
else
|
||||||
|
@machine.ui.info(I18n.t("vagrant.trigger.fire"))
|
||||||
|
end
|
||||||
|
|
||||||
|
if trigger.info
|
||||||
|
info(trigger.info)
|
||||||
|
end
|
||||||
|
|
||||||
|
if trigger.warn
|
||||||
|
warn(trigger.warn)
|
||||||
|
end
|
||||||
|
|
||||||
|
if trigger.run
|
||||||
|
run(trigger.run, trigger.on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
if trigger.run_remote
|
||||||
|
run_remote(trigger.run_remote, trigger.on_error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prints the given message at info level for a trigger
|
||||||
|
#
|
||||||
|
# @param [String] message The string to be printed
|
||||||
|
def info(message)
|
||||||
|
@machine.ui.info(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prints the given message at warn level for a trigger
|
||||||
|
#
|
||||||
|
# @param [String] message The string to be printed
|
||||||
|
def warn(message)
|
||||||
|
@machine.ui.warn(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Runs a script on a guest
|
||||||
|
#
|
||||||
|
# @param [Provisioners::Shell::Config] config A Shell provisioner config
|
||||||
|
def run(config, on_error)
|
||||||
|
if config.inline
|
||||||
|
cmd = Shellwords.split(config.inline)
|
||||||
|
|
||||||
|
@machine.ui.detail(I18n.t("vagrant.trigger.run.inline", command: config.inline))
|
||||||
|
else
|
||||||
|
cmd = File.expand_path(config.path, @env.root_path)
|
||||||
|
cmd << " #{config.args.join(' ' )}" if config.args
|
||||||
|
cmd = Shellwords.split(cmd)
|
||||||
|
|
||||||
|
@machine.ui.detail(I18n.t("vagrant.trigger.run.script", path: config.path))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pick an execution method to run the script or inline string with
|
||||||
|
# Default to Subprocess::Execute
|
||||||
|
exec_method = Vagrant::Util::Subprocess.method(:execute)
|
||||||
|
|
||||||
|
if Vagrant::Util::Platform.windows?
|
||||||
|
if config.inline
|
||||||
|
exec_method = Vagrant::Util::PowerShell.method(:execute_inline)
|
||||||
|
else
|
||||||
|
exec_method = Vagrant::Util::PowerShell.method(:execute)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
result = exec_method.call(*cmd, :notify => [:stdout, :stderr]) do |type,data|
|
||||||
|
options = {}
|
||||||
|
case type
|
||||||
|
when :stdout
|
||||||
|
options[:color] = :green if !config.keep_color
|
||||||
|
when :stderr
|
||||||
|
options[:color] = :red if !config.keep_color
|
||||||
|
end
|
||||||
|
|
||||||
|
@machine.ui.detail(data, options)
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
@machine.ui.error(I18n.t("vagrant.errors.triggers_run_fail"))
|
||||||
|
@machine.ui.error(e.message)
|
||||||
|
|
||||||
|
if on_error == :halt
|
||||||
|
@logger.debug("Trigger run encountered an error. Halting on error...")
|
||||||
|
raise e
|
||||||
|
else
|
||||||
|
@logger.debug("Trigger run encountered an error. Continuing on anyway...")
|
||||||
|
@machine.ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Runs a script on the guest
|
||||||
|
#
|
||||||
|
# @param [ShellProvisioner/Config] config A Shell provisioner config
|
||||||
|
def run_remote(config, on_error)
|
||||||
|
unless @machine.state.id == :running
|
||||||
|
if on_error == :halt
|
||||||
|
raise Errors::TriggersGuestNotRunning,
|
||||||
|
machine_name: @machine.name,
|
||||||
|
state: @machine.state.id
|
||||||
|
else
|
||||||
|
@machine.ui.error(I18n.t("vagrant.errors.triggers_guest_not_running",
|
||||||
|
machine_name: @machine.name,
|
||||||
|
state: @machine.state.id))
|
||||||
|
@machine.ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
prov = VagrantPlugins::Shell::Provisioner.new(@machine, config)
|
||||||
|
|
||||||
|
begin
|
||||||
|
prov.provision
|
||||||
|
rescue => e
|
||||||
|
@machine.ui.error(I18n.t("vagrant.errors.triggers_run_fail"))
|
||||||
|
|
||||||
|
if on_error == :halt
|
||||||
|
@logger.debug("Trigger run encountered an error. Halting on error...")
|
||||||
|
raise e
|
||||||
|
else
|
||||||
|
@logger.debug("Trigger run encountered an error. Continuing on anyway...")
|
||||||
|
@machine.ui.error(e.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -87,6 +87,27 @@ module Vagrant
|
||||||
return r.stdout.chomp
|
return r.stdout.chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Execute a powershell command and return a result
|
||||||
|
#
|
||||||
|
# @param [String] command PowerShell command to execute.
|
||||||
|
# @param [Hash] opts A collection of options for subprocess::execute
|
||||||
|
# @param [Block] block Ruby block
|
||||||
|
def self.execute_inline(*command, **opts, &block)
|
||||||
|
validate_install!
|
||||||
|
c = [
|
||||||
|
executable,
|
||||||
|
"-NoLogo",
|
||||||
|
"-NoProfile",
|
||||||
|
"-NonInteractive",
|
||||||
|
"-ExecutionPolicy", "Bypass",
|
||||||
|
"-Command",
|
||||||
|
command
|
||||||
|
].flatten.compact
|
||||||
|
c << opts
|
||||||
|
|
||||||
|
Subprocess.execute(*c, &block)
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the version of PowerShell that is installed.
|
# Returns the version of PowerShell that is installed.
|
||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
require "vagrant"
|
||||||
|
require File.expand_path("../vm_trigger", __FILE__)
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Kernel_V2
|
||||||
|
class TriggerConfig < Vagrant.plugin("2", :config)
|
||||||
|
# The TriggerConfig class is what gets called when a user
|
||||||
|
# defines a new trigger in their Vagrantfile. The two entry points are
|
||||||
|
# either `config.trigger.before` or `config.trigger.after`.
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@logger = Log4r::Logger.new("vagrant::config::trigger")
|
||||||
|
|
||||||
|
# Internal State
|
||||||
|
@_before_triggers = [] # An array of VagrantConfigTrigger objects
|
||||||
|
@_after_triggers = [] # An array of VagrantConfigTrigger objects
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# Trigger before/after functions
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Commands are expected to be ether:
|
||||||
|
# - splat
|
||||||
|
# + config.trigger.before :up, :destroy, :halt do |trigger|....
|
||||||
|
# - array
|
||||||
|
# + config.trigger.before [:up, :destroy, :halt] do |trigger|....
|
||||||
|
#
|
||||||
|
# Config is expected to be given as a block, or the last parameter as a hash
|
||||||
|
#
|
||||||
|
# - block
|
||||||
|
# + config.trigger.before :up, :destroy, :halt do |trigger|
|
||||||
|
# trigger.option = "option"
|
||||||
|
# end
|
||||||
|
# - hash
|
||||||
|
# + config.trigger.before :up, :destroy, :halt, options: "option"
|
||||||
|
|
||||||
|
# Reads in and parses Vagrant command whitelist and settings for a defined
|
||||||
|
# trigger
|
||||||
|
#
|
||||||
|
# @param [Symbol] command Vagrant command to create trigger on
|
||||||
|
# @param [Block] block The defined before block
|
||||||
|
def before(*command, &block)
|
||||||
|
command.flatten!
|
||||||
|
blk = block
|
||||||
|
|
||||||
|
if !block_given? && command.last.is_a?(Hash)
|
||||||
|
# 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
|
||||||
|
elsif !block_given?
|
||||||
|
raise Vagrant::Errors::TriggersNoBlockGiven,
|
||||||
|
command: command
|
||||||
|
end
|
||||||
|
|
||||||
|
command.each do |cmd|
|
||||||
|
trigger = create_trigger(cmd, blk)
|
||||||
|
@_before_triggers << trigger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads in and parses Vagrant command whitelist and settings for a defined
|
||||||
|
# trigger
|
||||||
|
#
|
||||||
|
# @param [Symbol] command Vagrant command to create trigger on
|
||||||
|
# @param [Block] block The defined after block
|
||||||
|
def after(*command, &block)
|
||||||
|
command.flatten!
|
||||||
|
blk = block
|
||||||
|
|
||||||
|
if !block_given? && command.last.is_a?(Hash)
|
||||||
|
# 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
|
||||||
|
elsif !block_given?
|
||||||
|
raise Vagrant::Errors::TriggersNoBlockGiven,
|
||||||
|
command: command
|
||||||
|
end
|
||||||
|
|
||||||
|
command.each do |cmd|
|
||||||
|
trigger = create_trigger(cmd, blk)
|
||||||
|
@_after_triggers << trigger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# Internal methods, don't call these.
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Creates a new trigger config. If a block is given, parse that block
|
||||||
|
# by calling it with the created trigger. Otherwise set the options if it's
|
||||||
|
# a hash.
|
||||||
|
#
|
||||||
|
# @param [Symbol] command Vagrant command to create trigger on
|
||||||
|
# @param [Block] block The defined config block
|
||||||
|
# @return [VagrantConfigTrigger]
|
||||||
|
def create_trigger(command, block)
|
||||||
|
trigger = VagrantConfigTrigger.new(command)
|
||||||
|
if block.is_a?(Hash)
|
||||||
|
trigger.set_options(block)
|
||||||
|
else
|
||||||
|
block.call(trigger, VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
return trigger
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge(other)
|
||||||
|
super.tap do |result|
|
||||||
|
new_before_triggers = []
|
||||||
|
new_after_triggers = []
|
||||||
|
other_defined_before_triggers = other.instance_variable_get(:@_before_triggers)
|
||||||
|
other_defined_after_triggers = other.instance_variable_get(:@_after_triggers)
|
||||||
|
|
||||||
|
@_before_triggers.each do |bt|
|
||||||
|
other_bft = other_defined_before_triggers.find { |o| bt.id == o.id }
|
||||||
|
if other_bft
|
||||||
|
# Override, take it
|
||||||
|
other_bft = bt.merge(other_bft)
|
||||||
|
|
||||||
|
# Preserve order, always
|
||||||
|
bt = other_bft
|
||||||
|
other_defined_before_triggers.delete(other_bft)
|
||||||
|
end
|
||||||
|
|
||||||
|
new_before_triggers << bt.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
other_defined_before_triggers.each do |obt|
|
||||||
|
new_before_triggers << obt.dup
|
||||||
|
end
|
||||||
|
result.instance_variable_set(:@_before_triggers, new_before_triggers)
|
||||||
|
|
||||||
|
@_after_triggers.each do |at|
|
||||||
|
other_aft = other_defined_after_triggers.find { |o| at.id == o.id }
|
||||||
|
if other_aft
|
||||||
|
# Override, take it
|
||||||
|
other_aft = at.merge(other_aft)
|
||||||
|
|
||||||
|
# Preserve order, always
|
||||||
|
at = other_aft
|
||||||
|
other_defined_after_triggers.delete(other_aft)
|
||||||
|
end
|
||||||
|
|
||||||
|
new_after_triggers << at.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
other_defined_after_triggers.each do |oat|
|
||||||
|
new_after_triggers << oat.dup
|
||||||
|
end
|
||||||
|
result.instance_variable_set(:@_after_triggers, new_after_triggers)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Iterates over all defined triggers and finalizes their config objects
|
||||||
|
def finalize!
|
||||||
|
if !@_before_triggers.empty?
|
||||||
|
@_before_triggers.map { |t| t.finalize! }
|
||||||
|
end
|
||||||
|
|
||||||
|
if !@_after_triggers.empty?
|
||||||
|
@_after_triggers.map { |t| t.finalize! }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validate Trigger Arrays
|
||||||
|
def validate(machine)
|
||||||
|
errors = _detected_errors
|
||||||
|
@_before_triggers.each do |bt|
|
||||||
|
error = bt.validate(machine)
|
||||||
|
errors.concat error if !error.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
@_after_triggers.each do |at|
|
||||||
|
error = at.validate(machine)
|
||||||
|
errors.concat error if !error.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
{"trigger" => errors}
|
||||||
|
end
|
||||||
|
|
||||||
|
# return [Array]
|
||||||
|
def before_triggers
|
||||||
|
@_before_triggers
|
||||||
|
end
|
||||||
|
|
||||||
|
# return [Array]
|
||||||
|
def after_triggers
|
||||||
|
@_after_triggers
|
||||||
|
end
|
||||||
|
|
||||||
|
# The String representation of this Trigger.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def to_s
|
||||||
|
"trigger"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,204 @@
|
||||||
|
require 'log4r'
|
||||||
|
require Vagrant.source_root.join('plugins/provisioners/shell/config')
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Kernel_V2
|
||||||
|
# Represents a single configured provisioner for a VM.
|
||||||
|
class VagrantConfigTrigger < Vagrant.plugin("2", :config)
|
||||||
|
# Defaults
|
||||||
|
DEFAULT_ON_ERROR = :halt
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# Config class for a given Trigger
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Internal unique name for this trigger
|
||||||
|
#
|
||||||
|
# Note: This is for internal use only.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_reader :id
|
||||||
|
|
||||||
|
# Name for the given Trigger. Defaults to nil.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :name
|
||||||
|
|
||||||
|
# Command to fire the trigger on
|
||||||
|
#
|
||||||
|
# @return [Symbol]
|
||||||
|
attr_reader :command
|
||||||
|
|
||||||
|
# A string to print at the WARN level
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :info
|
||||||
|
|
||||||
|
# A string to print at the WARN level
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :warn
|
||||||
|
|
||||||
|
# Determines what how a Trigger should behave if it runs into an error.
|
||||||
|
# Defaults to :halt, otherwise can only be set to :continue.
|
||||||
|
#
|
||||||
|
# @return [Symbol]
|
||||||
|
attr_accessor :on_error
|
||||||
|
|
||||||
|
# If set, will not run trigger for the configured Vagrant commands.
|
||||||
|
#
|
||||||
|
# @return [Symbol, Array]
|
||||||
|
attr_accessor :ignore
|
||||||
|
|
||||||
|
|
||||||
|
# If set, will only run trigger for guests that match keys for this parameter.
|
||||||
|
#
|
||||||
|
# @return [String, Regex, Array]
|
||||||
|
attr_accessor :only_on
|
||||||
|
|
||||||
|
# A local inline or file script to execute for the trigger
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
attr_accessor :run
|
||||||
|
|
||||||
|
# A remote inline or file script to execute for the trigger
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
attr_accessor :run_remote
|
||||||
|
|
||||||
|
def initialize(command)
|
||||||
|
@logger = Log4r::Logger.new("vagrant::config::vm::trigger::config")
|
||||||
|
|
||||||
|
@name = UNSET_VALUE
|
||||||
|
@info = UNSET_VALUE
|
||||||
|
@warn = UNSET_VALUE
|
||||||
|
@on_error = UNSET_VALUE
|
||||||
|
@ignore = UNSET_VALUE
|
||||||
|
@only_on = UNSET_VALUE
|
||||||
|
@run = UNSET_VALUE
|
||||||
|
@run_remote = UNSET_VALUE
|
||||||
|
|
||||||
|
# Internal options
|
||||||
|
@id = SecureRandom.uuid
|
||||||
|
@command = command.to_sym
|
||||||
|
|
||||||
|
@logger.debug("Trigger defined for command: #{command}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize!
|
||||||
|
# Ensure all config options are set to nil or default value if untouched
|
||||||
|
# by user
|
||||||
|
@name = nil if @name == UNSET_VALUE
|
||||||
|
@info = nil if @info == UNSET_VALUE
|
||||||
|
@warn = nil if @warn == UNSET_VALUE
|
||||||
|
@on_error = DEFAULT_ON_ERROR if @on_error == UNSET_VALUE
|
||||||
|
@ignore = [] if @ignore == UNSET_VALUE
|
||||||
|
@run = nil if @run == UNSET_VALUE
|
||||||
|
@run_remote = nil if @run_remote == UNSET_VALUE
|
||||||
|
@only_on = nil if @only_on == UNSET_VALUE
|
||||||
|
|
||||||
|
# these values are expected to always be an Array internally,
|
||||||
|
# but can be set as a single String or Symbol
|
||||||
|
#
|
||||||
|
# Guests are stored internally as strings
|
||||||
|
if @only_on
|
||||||
|
@only_on = Array(@only_on)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Commands must be stored internally as symbols
|
||||||
|
if @ignore
|
||||||
|
@ignore = Array(@ignore)
|
||||||
|
@ignore.map! { |i| i.to_sym }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert @run and @run_remote to be a "Shell provisioner" config
|
||||||
|
if @run && @run.is_a?(Hash)
|
||||||
|
# Powershell args and privileged for run commands is currently not supported
|
||||||
|
# so by default use empty string or false if unset. This helps the validate
|
||||||
|
# function determine if the setting was purposefully set, to print a warning
|
||||||
|
if !@run.key?(:powershell_args)
|
||||||
|
@run[:powershell_args] = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
if !@run.key?(:privileged)
|
||||||
|
@run[:privileged] = false
|
||||||
|
end
|
||||||
|
|
||||||
|
new_run = VagrantPlugins::Shell::Config.new
|
||||||
|
new_run.set_options(@run)
|
||||||
|
new_run.finalize!
|
||||||
|
@run = new_run
|
||||||
|
end
|
||||||
|
|
||||||
|
if @run_remote && @run_remote.is_a?(Hash)
|
||||||
|
new_run = VagrantPlugins::Shell::Config.new
|
||||||
|
new_run.set_options(@run_remote)
|
||||||
|
new_run.finalize!
|
||||||
|
@run_remote = new_run
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Array] array of strings of error messages from config option validation
|
||||||
|
def validate(machine)
|
||||||
|
errors = _detected_errors
|
||||||
|
|
||||||
|
commands = []
|
||||||
|
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
|
||||||
|
|
||||||
|
if @run
|
||||||
|
errorz = @run.validate(machine)
|
||||||
|
errors.concat errorz["shell provisioner"] if !errorz.empty?
|
||||||
|
|
||||||
|
if @run.privileged == true
|
||||||
|
machine.ui.warn(I18n.t("vagrant.config.triggers.privileged_ignored",
|
||||||
|
command: @command))
|
||||||
|
end
|
||||||
|
|
||||||
|
if @run.powershell_args != ""
|
||||||
|
machine.ui.warn(I18n.t("vagrant.config.triggers.powershell_args_ignored"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @run_remote
|
||||||
|
errorz = @run_remote.validate(machine)
|
||||||
|
errors.concat errorz["shell provisioner"] if !errorz.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
if @name && !@name.is_a?(String)
|
||||||
|
errors << I18n.t("vagrant.config.triggers.name_bad_type", cmd: @command)
|
||||||
|
end
|
||||||
|
|
||||||
|
if @info && !@info.is_a?(String)
|
||||||
|
errors << I18n.t("vagrant.config.triggers.info_bad_type", cmd: @command)
|
||||||
|
end
|
||||||
|
|
||||||
|
if @warn && !@warn.is_a?(String)
|
||||||
|
errors << I18n.t("vagrant.config.triggers.warn_bad_type", cmd: @command)
|
||||||
|
end
|
||||||
|
|
||||||
|
if @on_error != :halt
|
||||||
|
if @on_error != :continue
|
||||||
|
errors << I18n.t("vagrant.config.triggers.on_error_bad_type", cmd: @command)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
errors
|
||||||
|
end
|
||||||
|
|
||||||
|
# The String representation of this Trigger.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def to_s
|
||||||
|
"trigger config"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -39,6 +39,11 @@ module VagrantPlugins
|
||||||
require File.expand_path("../config/vm", __FILE__)
|
require File.expand_path("../config/vm", __FILE__)
|
||||||
VMConfig
|
VMConfig
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config("trigger") do
|
||||||
|
require File.expand_path("../config/trigger", __FILE__)
|
||||||
|
TriggerConfig
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -279,6 +279,24 @@ en:
|
||||||
up some disk space.
|
up some disk space.
|
||||||
|
|
||||||
Press the Enter or Return key to continue.
|
Press the Enter or Return key to continue.
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
on_error_continue: |-
|
||||||
|
Trigger configured to continue on error...
|
||||||
|
start: |-
|
||||||
|
Running triggers %{stage} %{action} ...
|
||||||
|
fire_with_name: |-
|
||||||
|
Running trigger: %{name}...
|
||||||
|
fire: |-
|
||||||
|
Running trigger...
|
||||||
|
run:
|
||||||
|
inline: |-
|
||||||
|
Running local: Inline script
|
||||||
|
%{command}
|
||||||
|
script: |-
|
||||||
|
Running local script: %{path}
|
||||||
|
|
||||||
|
|
||||||
version_current: |-
|
version_current: |-
|
||||||
Installed Version: %{version}
|
Installed Version: %{version}
|
||||||
version_latest: |-
|
version_latest: |-
|
||||||
|
@ -1392,6 +1410,22 @@ en:
|
||||||
The synced folder type '%{type}' is reporting as unusable for
|
The synced folder type '%{type}' is reporting as unusable for
|
||||||
your current setup. Please verify you have all the proper
|
your current setup. Please verify you have all the proper
|
||||||
prerequisites for using this shared folder type and try again.
|
prerequisites for using this shared folder type and try again.
|
||||||
|
|
||||||
|
triggers_run_fail: |-
|
||||||
|
Trigger run failed
|
||||||
|
triggers_guest_not_running: |-
|
||||||
|
Could not run remote script on %{machine_name} because its state is %{state}
|
||||||
|
triggers_no_block_given: |-
|
||||||
|
There was an error parsing the Vagrantfile:
|
||||||
|
No config was given for the trigger(s) %{command}.
|
||||||
|
triggers_no_stage_given: |-
|
||||||
|
The incorrect stage was given to the trigger plugin:
|
||||||
|
Guest: %{guest_name}
|
||||||
|
Action: %{action}
|
||||||
|
Stage: %{stage}
|
||||||
|
|
||||||
|
This is an internal error that should be reported as a bug.
|
||||||
|
|
||||||
ui_expects_tty: |-
|
ui_expects_tty: |-
|
||||||
Vagrant is attempting to interface with the UI in a way that requires
|
Vagrant is attempting to interface with the UI in a way that requires
|
||||||
a TTY. Most actions in Vagrant that require a TTY have configuration
|
a TTY. Most actions in Vagrant that require a TTY have configuration
|
||||||
|
@ -1677,6 +1711,33 @@ en:
|
||||||
paranoid_deprecated: |-
|
paranoid_deprecated: |-
|
||||||
The key `paranoid` is deprecated. Please use `verify_host_key`. Supported
|
The key `paranoid` is deprecated. Please use `verify_host_key`. Supported
|
||||||
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.
|
||||||
|
triggers:
|
||||||
|
bad_command_warning: |-
|
||||||
|
The command '%{cmd}' was not found for this trigger.
|
||||||
|
name_bad_type: |-
|
||||||
|
Invalid type set for `name` on trigger for command '%{cmd}'. `name` should be a String.
|
||||||
|
info_bad_type: |-
|
||||||
|
Invalid type set for `info` on trigger for command '%{cmd}'. `info` should be a String.
|
||||||
|
warn_bad_type: |-
|
||||||
|
Invalid type set for `warn` on trigger for command '%{cmd}'. `warn` should be a String.
|
||||||
|
on_error_bad_type: |-
|
||||||
|
Invalid type set for `on_error` on trigger for command '%{cmd}'. `on_error` can
|
||||||
|
only be `:halt` (default) or `:continue`.
|
||||||
|
only_on_bad_type: |-
|
||||||
|
Invalid type found for `only_on`. All values must be a `String` or `Regexp`.
|
||||||
|
privileged_ignored: |-
|
||||||
|
The `privileged` setting for option `run` for trigger command '%{command}' will be ignored and set to false.
|
||||||
|
powershell_args_ignored: |-
|
||||||
|
The setting `powershell_args` is not supported for the trigger option `run` and will be ignored.
|
||||||
|
run:
|
||||||
|
bad_type: |-
|
||||||
|
Invalid type set for `run` on trigger for command '%{cmd}'. `run`
|
||||||
|
must be a Hash.
|
||||||
|
run_remote:
|
||||||
|
bad_type: |-
|
||||||
|
Invalid type set for `run` on trigger for command '%{cmd}'. `run`
|
||||||
|
must be a Hash.
|
||||||
|
|
||||||
vm:
|
vm:
|
||||||
bad_version: |-
|
bad_version: |-
|
||||||
Invalid box version constraints: %{version}
|
Invalid box version constraints: %{version}
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
require File.expand_path("../../../../base", __FILE__)
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/kernel_v2/config/trigger")
|
||||||
|
|
||||||
|
describe VagrantPlugins::Kernel_V2::TriggerConfig do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
subject { described_class.new }
|
||||||
|
|
||||||
|
let(:machine) { double("machine") }
|
||||||
|
|
||||||
|
def assert_invalid
|
||||||
|
errors = subject.validate(machine)
|
||||||
|
if !errors.values.any? { |v| !v.empty? }
|
||||||
|
raise "No errors: #{errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_valid
|
||||||
|
errors = subject.validate(machine)
|
||||||
|
if !errors.values.all? { |v| v.empty? }
|
||||||
|
raise "Errors: #{errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
env = double("env")
|
||||||
|
allow(env).to receive(:root_path).and_return(nil)
|
||||||
|
allow(machine).to receive(:env).and_return(env)
|
||||||
|
allow(machine).to receive(:provider_config).and_return(nil)
|
||||||
|
allow(machine).to receive(:provider_options).and_return({})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is valid with test defaults" do
|
||||||
|
subject.finalize!
|
||||||
|
assert_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
let (:hash_block) { {info: "hi", run: {inline: "echo 'hi'"}} }
|
||||||
|
let (:splat) { [:up, :destroy, :halt] }
|
||||||
|
let (:arr) { [[:up, :destroy, :halt]] }
|
||||||
|
|
||||||
|
describe "creating a before trigger" do
|
||||||
|
it "creates a trigger with the splat syntax" do
|
||||||
|
subject.before(:up, hash_block)
|
||||||
|
bf_trigger = subject.instance_variable_get(:@_before_triggers)
|
||||||
|
expect(bf_trigger.size).to eq(1)
|
||||||
|
expect(bf_trigger.first).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a trigger with the array syntax" do
|
||||||
|
subject.before([:up], hash_block)
|
||||||
|
bf_trigger = subject.instance_variable_get(:@_before_triggers)
|
||||||
|
expect(bf_trigger.size).to eq(1)
|
||||||
|
expect(bf_trigger.first).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a trigger with the block syntax" do
|
||||||
|
subject.before :up do |trigger|
|
||||||
|
trigger.name = "rspec"
|
||||||
|
end
|
||||||
|
bf_trigger = subject.instance_variable_get(:@_before_triggers)
|
||||||
|
expect(bf_trigger.size).to eq(1)
|
||||||
|
expect(bf_trigger.first).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates multiple triggers with the splat syntax" do
|
||||||
|
subject.before(splat, hash_block)
|
||||||
|
bf_trigger = subject.instance_variable_get(:@_before_triggers)
|
||||||
|
expect(bf_trigger.size).to eq(3)
|
||||||
|
bf_trigger.map { |t| expect(t).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates multiple triggers with the block syntax" do
|
||||||
|
subject.before splat do |trigger|
|
||||||
|
trigger.name = "rspec"
|
||||||
|
end
|
||||||
|
bf_trigger = subject.instance_variable_get(:@_before_triggers)
|
||||||
|
expect(bf_trigger.size).to eq(3)
|
||||||
|
bf_trigger.map { |t| expect(t).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates multiple triggers with the array syntax" do
|
||||||
|
subject.before(arr, hash_block)
|
||||||
|
bf_trigger = subject.instance_variable_get(:@_before_triggers)
|
||||||
|
expect(bf_trigger.size).to eq(3)
|
||||||
|
bf_trigger.map { |t| expect(t).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "creating an after trigger" do
|
||||||
|
it "creates a trigger with the splat syntax" do
|
||||||
|
subject.after(:up, hash_block)
|
||||||
|
af_trigger = subject.instance_variable_get(:@_after_triggers)
|
||||||
|
expect(af_trigger.size).to eq(1)
|
||||||
|
expect(af_trigger.first).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a trigger with the array syntax" do
|
||||||
|
subject.after([:up], hash_block)
|
||||||
|
af_trigger = subject.instance_variable_get(:@_after_triggers)
|
||||||
|
expect(af_trigger.size).to eq(1)
|
||||||
|
expect(af_trigger.first).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a trigger with the block syntax" do
|
||||||
|
subject.after :up do |trigger|
|
||||||
|
trigger.name = "rspec"
|
||||||
|
end
|
||||||
|
af_trigger = subject.instance_variable_get(:@_after_triggers)
|
||||||
|
expect(af_trigger.size).to eq(1)
|
||||||
|
expect(af_trigger.first).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates multiple triggers with the splat syntax" do
|
||||||
|
subject.after(splat, hash_block)
|
||||||
|
af_trigger = subject.instance_variable_get(:@_after_triggers)
|
||||||
|
expect(af_trigger.size).to eq(3)
|
||||||
|
af_trigger.map { |t| expect(t).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates multiple triggers with the block syntax" do
|
||||||
|
subject.after splat do |trigger|
|
||||||
|
trigger.name = "rspec"
|
||||||
|
end
|
||||||
|
af_trigger = subject.instance_variable_get(:@_after_triggers)
|
||||||
|
expect(af_trigger.size).to eq(3)
|
||||||
|
af_trigger.map { |t| expect(t).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates multiple triggers with the array syntax" do
|
||||||
|
subject.after(arr, hash_block)
|
||||||
|
af_trigger = subject.instance_variable_get(:@_after_triggers)
|
||||||
|
expect(af_trigger.size).to eq(3)
|
||||||
|
af_trigger.map { |t| expect(t).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#create_trigger" do
|
||||||
|
let(:command) { :up }
|
||||||
|
let(:hash_block) { {info: "hi", run: {inline: "echo 'hi'"}} }
|
||||||
|
|
||||||
|
it "returns a new VagrantConfigTrigger object if given a hash" do
|
||||||
|
trigger = subject.create_trigger(command, hash_block)
|
||||||
|
expect(trigger).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a new VagrantConfigTrigger object if given a block" do
|
||||||
|
block = Proc.new { |b| b.info = "test"}
|
||||||
|
|
||||||
|
trigger = subject.create_trigger(command, block)
|
||||||
|
expect(trigger).to be_a(VagrantPlugins::Kernel_V2::VagrantConfigTrigger)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#merge" do
|
||||||
|
it "merges defined triggers" do
|
||||||
|
a = described_class.new()
|
||||||
|
b = described_class.new()
|
||||||
|
|
||||||
|
a.before(splat, hash_block)
|
||||||
|
a.after(arr, hash_block)
|
||||||
|
b.before(splat, hash_block)
|
||||||
|
b.after(arr, hash_block)
|
||||||
|
|
||||||
|
result = a.merge(b)
|
||||||
|
bf_trigger = result.instance_variable_get(:@_before_triggers)
|
||||||
|
af_trigger = result.instance_variable_get(:@_after_triggers)
|
||||||
|
|
||||||
|
expect(bf_trigger).to be_a(Array)
|
||||||
|
expect(af_trigger).to be_a(Array)
|
||||||
|
expect(bf_trigger.size).to eq(6)
|
||||||
|
expect(af_trigger.size).to eq(6)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "merges the other triggers if a class is empty" do
|
||||||
|
a = described_class.new()
|
||||||
|
b = described_class.new()
|
||||||
|
|
||||||
|
a.before(splat, hash_block)
|
||||||
|
a.after(arr, hash_block)
|
||||||
|
|
||||||
|
b_bf_trigger = b.instance_variable_get(:@_before_triggers)
|
||||||
|
b_af_trigger = b.instance_variable_get(:@_after_triggers)
|
||||||
|
|
||||||
|
result = a.merge(b)
|
||||||
|
bf_trigger = result.instance_variable_get(:@_before_triggers)
|
||||||
|
af_trigger = result.instance_variable_get(:@_after_triggers)
|
||||||
|
|
||||||
|
expect(bf_trigger.size).to eq(3)
|
||||||
|
expect(af_trigger.size).to eq(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,124 @@
|
||||||
|
require File.expand_path("../../../../base", __FILE__)
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/kernel_v2/config/vm_trigger")
|
||||||
|
|
||||||
|
describe VagrantPlugins::Kernel_V2::VagrantConfigTrigger do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
let(:command) { :up }
|
||||||
|
|
||||||
|
subject { described_class.new(command) }
|
||||||
|
|
||||||
|
let(:machine) { double("machine") }
|
||||||
|
|
||||||
|
def assert_invalid
|
||||||
|
errors = subject.validate(machine)
|
||||||
|
if !errors.empty? { |v| !v.empty? }
|
||||||
|
raise "No errors: #{errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_valid
|
||||||
|
errors = subject.validate(machine)
|
||||||
|
if !errors.empty? { |v| v.empty? }
|
||||||
|
raise "Errors: #{errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
env = double("env")
|
||||||
|
allow(env).to receive(:root_path).and_return(nil)
|
||||||
|
allow(machine).to receive(:env).and_return(env)
|
||||||
|
allow(machine).to receive(:provider_config).and_return(nil)
|
||||||
|
allow(machine).to receive(:provider_options).and_return({})
|
||||||
|
|
||||||
|
subject.name = "foo"
|
||||||
|
subject.info = "Hello there"
|
||||||
|
subject.warn = "Warning!!"
|
||||||
|
subject.ignore = :up
|
||||||
|
subject.only_on = "guest"
|
||||||
|
subject.run = {inline: "apt-get update"}
|
||||||
|
subject.run_remote = {inline: "apt-get update", env: {"VAR"=>"VAL"}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with defaults" do
|
||||||
|
it "is valid with test defaults" do
|
||||||
|
subject.finalize!
|
||||||
|
assert_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets a command" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.command).to eq(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses default error behavior" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.on_error).to eq(:halt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "defining a new config that needs to match internal restraints" do
|
||||||
|
let(:cmd) { :destroy }
|
||||||
|
let(:cfg) { described_class.new(cmd) }
|
||||||
|
let(:arr_cfg) { described_class.new(cmd) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
cfg.only_on = :guest
|
||||||
|
cfg.ignore = "up"
|
||||||
|
arr_cfg.only_on = ["guest", /other/]
|
||||||
|
arr_cfg.ignore = ["up", "destroy"]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ensures only_on is an array" do
|
||||||
|
cfg.finalize!
|
||||||
|
arr_cfg.finalize!
|
||||||
|
|
||||||
|
expect(cfg.only_on).to be_a(Array)
|
||||||
|
expect(arr_cfg.only_on).to be_a(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "ensures ignore is an array of symbols" do
|
||||||
|
cfg.finalize!
|
||||||
|
arr_cfg.finalize!
|
||||||
|
|
||||||
|
expect(cfg.ignore).to be_a(Array)
|
||||||
|
expect(arr_cfg.ignore).to be_a(Array)
|
||||||
|
|
||||||
|
cfg.ignore.each do |a|
|
||||||
|
expect(a).to be_a(Symbol)
|
||||||
|
end
|
||||||
|
|
||||||
|
arr_cfg.ignore.each do |a|
|
||||||
|
expect(a).to be_a(Symbol)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "defining a basic trigger config" do
|
||||||
|
let(:cmd) { :up }
|
||||||
|
let(:cfg) { described_class.new(cmd) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
cfg.info = "Hello there"
|
||||||
|
cfg.warn = "Warning!!"
|
||||||
|
cfg.on_error = :continue
|
||||||
|
cfg.ignore = :up
|
||||||
|
cfg.only_on = "guest"
|
||||||
|
cfg.run = {inline: "apt-get update"}
|
||||||
|
cfg.run_remote = {inline: "apt-get update", env: {"VAR"=>"VAL"}}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets the options" do
|
||||||
|
cfg.finalize!
|
||||||
|
expect(cfg.info).to eq("Hello there")
|
||||||
|
expect(cfg.warn).to eq("Warning!!")
|
||||||
|
expect(cfg.on_error).to eq(:continue)
|
||||||
|
expect(cfg.ignore).to eq([:up])
|
||||||
|
expect(cfg.only_on).to eq(["guest"])
|
||||||
|
expect(cfg.run).to be_a(VagrantPlugins::Shell::Config)
|
||||||
|
expect(cfg.run_remote).to be_a(VagrantPlugins::Shell::Config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,319 @@
|
||||||
|
require File.expand_path("../../../../base", __FILE__)
|
||||||
|
require Vagrant.source_root.join("plugins/kernel_v2/config/trigger")
|
||||||
|
|
||||||
|
describe Vagrant::Plugin::V2::Trigger do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
let(:iso_env) do
|
||||||
|
# We have to create a Vagrantfile so there is a root path
|
||||||
|
isolated_environment.tap do |env|
|
||||||
|
env.vagrantfile("")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
let(:iso_vagrant_env) { iso_env.create_vagrant_env }
|
||||||
|
let(:state) { double("state", id: :running) }
|
||||||
|
let(:machine) do
|
||||||
|
iso_vagrant_env.machine(iso_vagrant_env.machine_names[0], :dummy).tap do |m|
|
||||||
|
allow(m).to receive(:state).and_return(state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
let(:env) { {
|
||||||
|
machine: machine,
|
||||||
|
ui: Vagrant::UI::Silent.new,
|
||||||
|
} }
|
||||||
|
|
||||||
|
let(:triggers) { VagrantPlugins::Kernel_V2::TriggerConfig.new }
|
||||||
|
let(:hash_block) { {info: "hi", run: {inline: "echo 'hi'"}} }
|
||||||
|
let(:hash_block_two) { {warn: "WARNING!!", run_remote: {inline: "echo 'hi'"}} }
|
||||||
|
|
||||||
|
before do
|
||||||
|
triggers.before(:up, hash_block)
|
||||||
|
triggers.before(:destroy, hash_block)
|
||||||
|
triggers.before(:halt, hash_block_two)
|
||||||
|
triggers.after(:up, hash_block)
|
||||||
|
triggers.after(:destroy, hash_block)
|
||||||
|
triggers.finalize!
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
let(:subject) { described_class.new(env, triggers, machine) }
|
||||||
|
|
||||||
|
context "#fire_triggers" do
|
||||||
|
it "raises an error if an inproper stage is given" do
|
||||||
|
expect{ subject.fire_triggers(:up, :not_real, "guest") }.
|
||||||
|
to raise_error(Vagrant::Errors::TriggersNoStageGiven)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#filter_triggers" do
|
||||||
|
it "returns all triggers if no constraints" do
|
||||||
|
before_triggers = triggers.before_triggers
|
||||||
|
filtered_triggers = subject.send(:filter_triggers, before_triggers, "guest")
|
||||||
|
expect(filtered_triggers).to eq(before_triggers)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "filters a trigger if it doesn't match guest_name" do
|
||||||
|
trigger_config = {info: "no", only_on: "notrealguest"}
|
||||||
|
triggers.after(:up, trigger_config)
|
||||||
|
triggers.finalize!
|
||||||
|
|
||||||
|
after_triggers = triggers.after_triggers
|
||||||
|
expect(after_triggers.size).to eq(3)
|
||||||
|
subject.send(:filter_triggers, after_triggers, "ubuntu")
|
||||||
|
expect(after_triggers.size).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "keeps a trigger that has a restraint that matches guest name" do
|
||||||
|
trigger_config = {info: "no", only_on: /guest/}
|
||||||
|
triggers.after(:up, trigger_config)
|
||||||
|
triggers.finalize!
|
||||||
|
|
||||||
|
after_triggers = triggers.after_triggers
|
||||||
|
expect(after_triggers.size).to eq(3)
|
||||||
|
subject.send(:filter_triggers, after_triggers, "ubuntu-guest")
|
||||||
|
expect(after_triggers.size).to eq(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "keeps a trigger that has multiple restraints that matches guest name" do
|
||||||
|
trigger_config = {info: "no", only_on: ["debian", /guest/]}
|
||||||
|
triggers.after(:up, trigger_config)
|
||||||
|
triggers.finalize!
|
||||||
|
|
||||||
|
after_triggers = triggers.after_triggers
|
||||||
|
expect(after_triggers.size).to eq(3)
|
||||||
|
subject.send(:filter_triggers, after_triggers, "ubuntu-guest")
|
||||||
|
expect(after_triggers.size).to eq(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#fire" do
|
||||||
|
it "calls the corresponding trigger methods if options set" do
|
||||||
|
expect(subject).to receive(:info).twice
|
||||||
|
expect(subject).to receive(:warn).once
|
||||||
|
expect(subject).to receive(:run).twice
|
||||||
|
expect(subject).to receive(:run_remote).once
|
||||||
|
subject.send(:fire, triggers.before_triggers, "guest")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#info" do
|
||||||
|
let(:message) { "Printing some info" }
|
||||||
|
|
||||||
|
it "prints messages at INFO" do
|
||||||
|
output = ""
|
||||||
|
allow(machine.ui).to receive(:info) do |data|
|
||||||
|
output << data
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.send(:info, message)
|
||||||
|
expect(output).to include(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#warn" do
|
||||||
|
let(:message) { "Printing some warnings" }
|
||||||
|
|
||||||
|
it "prints messages at WARN" do
|
||||||
|
output = ""
|
||||||
|
allow(machine.ui).to receive(:warn) do |data|
|
||||||
|
output << data
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.send(:warn, message)
|
||||||
|
expect(output).to include(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#run" do
|
||||||
|
let(:trigger_run) { VagrantPlugins::Kernel_V2::TriggerConfig.new }
|
||||||
|
let(:shell_block) { {info: "hi", run: {inline: "echo 'hi'", env: {"KEY"=>"VALUE"}}} }
|
||||||
|
let(:path_block) { {warn: "bye",
|
||||||
|
run: {path: "script.sh", env: {"KEY"=>"VALUE"}},
|
||||||
|
on_error: :continue} }
|
||||||
|
|
||||||
|
let(:path_block_ps1) { {warn: "bye",
|
||||||
|
run: {path: "script.ps1", env: {"KEY"=>"VALUE"}},
|
||||||
|
on_error: :continue} }
|
||||||
|
|
||||||
|
let(:exit_code) { 0 }
|
||||||
|
let(:options) { {:notify=>[:stdout, :stderr]} }
|
||||||
|
|
||||||
|
let(:subprocess_result) do
|
||||||
|
double("subprocess_result").tap do |result|
|
||||||
|
allow(result).to receive(:exit_code).and_return(exit_code)
|
||||||
|
allow(result).to receive(:stderr).and_return("")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
trigger_run.after(:up, shell_block)
|
||||||
|
trigger_run.before(:destroy, path_block)
|
||||||
|
trigger_run.before(:destroy, path_block_ps1)
|
||||||
|
trigger_run.finalize!
|
||||||
|
end
|
||||||
|
|
||||||
|
it "executes an inline script with powershell if windows" do
|
||||||
|
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true)
|
||||||
|
allow(Vagrant::Util::PowerShell).to receive(:execute_inline).
|
||||||
|
and_return(subprocess_result)
|
||||||
|
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect(Vagrant::Util::PowerShell).to receive(:execute_inline).
|
||||||
|
with("echo", "hi", options)
|
||||||
|
subject.send(:run, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "executes an path script with powershell if windows" do
|
||||||
|
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true)
|
||||||
|
allow(Vagrant::Util::PowerShell).to receive(:execute).
|
||||||
|
and_return(subprocess_result)
|
||||||
|
allow(env).to receive(:root_path).and_return("/vagrant/home")
|
||||||
|
|
||||||
|
trigger = trigger_run.before_triggers[1]
|
||||||
|
shell_config = trigger.run
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect(Vagrant::Util::PowerShell).to receive(:execute).
|
||||||
|
with("/vagrant/home/script.ps1", options)
|
||||||
|
subject.send(:run, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "executes an inline script" do
|
||||||
|
allow(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
and_return(subprocess_result)
|
||||||
|
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
with("echo", "hi", options)
|
||||||
|
subject.send(:run, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "executes an path script" do
|
||||||
|
allow(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
and_return(subprocess_result)
|
||||||
|
allow(env).to receive(:root_path).and_return("/vagrant/home")
|
||||||
|
allow(FileUtils).to receive(:chmod).and_return(true)
|
||||||
|
|
||||||
|
trigger = trigger_run.before_triggers.first
|
||||||
|
shell_config = trigger.run
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
with("/vagrant/home/script.sh", options)
|
||||||
|
subject.send(:run, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "continues on error" do
|
||||||
|
allow(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
and_raise("Fail!")
|
||||||
|
allow(env).to receive(:root_path).and_return("/vagrant/home")
|
||||||
|
allow(FileUtils).to receive(:chmod).and_return(true)
|
||||||
|
|
||||||
|
trigger = trigger_run.before_triggers.first
|
||||||
|
shell_config = trigger.run
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
with("/vagrant/home/script.sh", options)
|
||||||
|
subject.send(:run, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "halts on error" do
|
||||||
|
allow(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
and_raise("Fail!")
|
||||||
|
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect(Vagrant::Util::Subprocess).to receive(:execute).
|
||||||
|
with("echo", "hi", options)
|
||||||
|
expect { subject.send(:run, shell_config, on_error) }.to raise_error("Fail!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "#run_remote" do
|
||||||
|
let (:trigger_run) { VagrantPlugins::Kernel_V2::TriggerConfig.new }
|
||||||
|
let (:shell_block) { {info: "hi", run_remote: {inline: "echo 'hi'", env: {"KEY"=>"VALUE"}}} }
|
||||||
|
let (:path_block) { {warn: "bye",
|
||||||
|
run_remote: {path: "script.sh", env: {"KEY"=>"VALUE"}},
|
||||||
|
on_error: :continue} }
|
||||||
|
let(:provision) { double("provision") }
|
||||||
|
|
||||||
|
before do
|
||||||
|
trigger_run.after(:up, shell_block)
|
||||||
|
trigger_run.before(:destroy, path_block)
|
||||||
|
trigger_run.finalize!
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error and halts if guest is not running" do
|
||||||
|
allow(machine.state).to receive(:id).and_return(:not_running)
|
||||||
|
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect { subject.send(:run_remote, shell_config, on_error) }.
|
||||||
|
to raise_error(Vagrant::Errors::TriggersGuestNotRunning)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "continues on if guest is not running but is configured to continue on error" do
|
||||||
|
allow(machine.state).to receive(:id).and_return(:not_running)
|
||||||
|
|
||||||
|
allow(env).to receive(:root_path).and_return("/vagrant/home")
|
||||||
|
allow(FileUtils).to receive(:chmod).and_return(true)
|
||||||
|
|
||||||
|
trigger = trigger_run.before_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
subject.send(:run_remote, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls the provision function on the shell provisioner" do
|
||||||
|
allow(machine.state).to receive(:id).and_return(:running)
|
||||||
|
allow(provision).to receive(:provision).and_return("Provision!")
|
||||||
|
allow(VagrantPlugins::Shell::Provisioner).to receive(:new).
|
||||||
|
and_return(provision)
|
||||||
|
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
subject.send(:run_remote, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "continues on if provision fails" do
|
||||||
|
allow(machine.state).to receive(:id).and_return(:running)
|
||||||
|
allow(provision).to receive(:provision).and_raise("Nope!")
|
||||||
|
allow(VagrantPlugins::Shell::Provisioner).to receive(:new).
|
||||||
|
and_return(provision)
|
||||||
|
|
||||||
|
trigger = trigger_run.before_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
subject.send(:run_remote, shell_config, on_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "fails if it encounters an error" do
|
||||||
|
allow(machine.state).to receive(:id).and_return(:running)
|
||||||
|
allow(provision).to receive(:provision).and_raise("Nope!")
|
||||||
|
allow(VagrantPlugins::Shell::Provisioner).to receive(:new).
|
||||||
|
and_return(provision)
|
||||||
|
|
||||||
|
trigger = trigger_run.after_triggers.first
|
||||||
|
shell_config = trigger.run_remote
|
||||||
|
on_error = trigger.on_error
|
||||||
|
|
||||||
|
expect { subject.send(:run_remote, shell_config, on_error) }.
|
||||||
|
to raise_error("Nope!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Vagrant Triggers Configuration"
|
||||||
|
sidebar_current: "triggers-configuration"
|
||||||
|
description: |-
|
||||||
|
Documentation of various configuration options for Vagrant Triggers
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Vagrant Triggers has a few options to define trigger behavior.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
The trigger class takes various options.
|
||||||
|
|
||||||
|
* `action` (symbol, array) - Expected to be a single symbol value, an array of symbols, or a _splat_ of symbols. The first argument that comes after either __before__ or __after__ when defining a new trigger. Can be any valid Vagrant command. It also accepts a special value `:all` which will make the trigger fire for every action. An action can be ignored with the `ignore` setting if desired. These are the valid action commands for triggers:
|
||||||
|
|
||||||
|
- `destroy`
|
||||||
|
- `halt`
|
||||||
|
- `provision`
|
||||||
|
- `reload`
|
||||||
|
- `resume`
|
||||||
|
- `up`
|
||||||
|
|
||||||
|
* `ignore` (symbol, array) - Symbol or array of symbols corresponding to the action that a trigger should not fire on.
|
||||||
|
|
||||||
|
* `info` (string) - A message that will be printed at the beginning of a trigger.
|
||||||
|
|
||||||
|
* `name` (string) - The name of the trigger. If set, the name will be displayed when firing the trigger.
|
||||||
|
|
||||||
|
* `on_error` (symbol) - Defines how the trigger should behave if it encounters an error. By default this will be `:halt`, but can be configured to ignore failures and continue on with `:continue`.
|
||||||
|
|
||||||
|
* `only_on` (string, regex, array) - Guest or guests to be ignored on the defined trigger. Values can be a string or regex that matches a guest name.
|
||||||
|
|
||||||
|
* `run_remote` (hash) - A collection of settings to run a inline or remote script with on the guest. These settings correspond to the [shell provosioner](/docs/provisioning/shell.html).
|
||||||
|
|
||||||
|
* `run` (hash) - A collection of settings to run a inline or remote script with on the host. These settings correspond to the [shell provosioner](/docs/provisioning/shell.html). However, at the moment the only settings `run` takes advantage of are:
|
||||||
|
+ `args`
|
||||||
|
+ `inline`
|
||||||
|
+ `path`
|
||||||
|
|
||||||
|
* `warn` (string) - A warning message that will be printed at the beginning of a trigger.
|
|
@ -0,0 +1,95 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Vagrant Triggers"
|
||||||
|
sidebar_current: "triggers"
|
||||||
|
description: |-
|
||||||
|
Introduction to Vagrant Triggers
|
||||||
|
---
|
||||||
|
|
||||||
|
# Vagrant Triggers
|
||||||
|
|
||||||
|
As of version 2.1.0, Vagrant is capable of executing machine triggers _before_ or
|
||||||
|
_after_ Vagrant commands.
|
||||||
|
|
||||||
|
Each trigger is expected to be given a command key for when it should be fired
|
||||||
|
during the Vagrant command lifecycle. These could be defined as a single key or
|
||||||
|
an array which acts like a _whitelist_ for the defined trigger.
|
||||||
|
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# single command trigger
|
||||||
|
config.trigger.after :up do |trigger|
|
||||||
|
...
|
||||||
|
end
|
||||||
|
|
||||||
|
# multiple commands for this trigger
|
||||||
|
config.trigger.before [:up, :destroy, :halt, :package] do |trigger|
|
||||||
|
...
|
||||||
|
end
|
||||||
|
|
||||||
|
# or defined as a splat list
|
||||||
|
config.trigger.before :up, :destroy, :halt, :package do |trigger|
|
||||||
|
...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, the key `:all` could be given which would run the trigger before
|
||||||
|
or after every Vagrant command. If there is a command you don't want the trigger
|
||||||
|
to run on, you can ignore that command with the `ignore` option.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# single command trigger
|
||||||
|
config.trigger.before :all do |trigger|
|
||||||
|
trigger.info = "Running a before trigger!"
|
||||||
|
trigger.ignore = [:destroy, :halt]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
__Note:__ _If a trigger is defined on a command that does not exist, a warning
|
||||||
|
will be displayed._
|
||||||
|
|
||||||
|
Triggers can be defined as a block or hash in a Vagrantfile. The example below
|
||||||
|
will result in the same trigger:
|
||||||
|
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.trigger.after :up do |trigger|
|
||||||
|
trigger.name = "Finished Message"
|
||||||
|
trigger.info = "Machine is up!"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.trigger.after :up,
|
||||||
|
name: "Finished Message",
|
||||||
|
info: "Machine is up!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Triggers can also be defined within the scope of guests in a Vagrantfile. These
|
||||||
|
triggers will only run on the configured guest. An example of a guest only trigger:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
config.vm.define "ubuntu" do |ubuntu|
|
||||||
|
ubuntu.vm.box = "ubuntu"
|
||||||
|
ubuntu.trigger.before :destroy do |trigger|
|
||||||
|
trigger.warn = "Dumping database to /vagrant/outfile"
|
||||||
|
trigger.run_remote {inline: "pg_dump dbname > /vagrant/outfile"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Global and machine-scoped triggers will execute in the order that they are
|
||||||
|
defined within a Vagrantfile. Take for example an abstracted Vagrantfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
Vagrantfile
|
||||||
|
global trigger 1
|
||||||
|
global trigger 2
|
||||||
|
machine defined
|
||||||
|
machine trigger 3
|
||||||
|
global trigger 4
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
In this generic case, the triggers would fire in the order: 1 -> 2 -> 3 -> 4
|
||||||
|
|
||||||
|
For more information about what options are available for triggers, see the
|
||||||
|
[configuration section](/docs/triggers/configuration.html).
|
|
@ -0,0 +1,95 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Vagrant Triggers Usage"
|
||||||
|
sidebar_current: "triggers-usage"
|
||||||
|
description: |-
|
||||||
|
Various Vagrant Triggers examples
|
||||||
|
---
|
||||||
|
|
||||||
|
# Basic Usage
|
||||||
|
|
||||||
|
Below are some very simple examples of how to use Vagrant Triggers.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
The following is a basic example of two global triggers. One that runs _before_
|
||||||
|
the `:up` command and one that runs _after_ the `:up` command:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.trigger.before :up do |trigger|
|
||||||
|
trigger.name = "Hello world"
|
||||||
|
trigger.info = "I am running before vagrant up!!"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.trigger.before :up do |trigger|
|
||||||
|
trigger.name = "Hello world"
|
||||||
|
trigger.info = "I am running after vagrant up!!"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.define "ubuntu" do |ubuntu|
|
||||||
|
ubuntu.vm.box = "ubuntu"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
These will run before and after each defined guest in the Vagrantfile.
|
||||||
|
|
||||||
|
Running a remote script to save a database on your host before __destroy__ing a
|
||||||
|
guest:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.define "ubuntu" do |ubuntu|
|
||||||
|
ubuntu.vm.box = "ubuntu"
|
||||||
|
|
||||||
|
ubuntu.trigger.before :destroy do |trigger|
|
||||||
|
trigger.warn = "Dumping database to /vagrant/outfile"
|
||||||
|
trigger.run_remote = {inline: "pg_dump dbname > /vagrant/outfile"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that the trigger is defined, running the __destroy__ command will fire off
|
||||||
|
the defined trigger before Vagrant destroys the machine.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ vagrant destroy ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
An example of defining three triggers that start and stop tinyproxy on your host
|
||||||
|
machine using homebrew:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
#/bin/bash
|
||||||
|
# start-tinyproxy.sh
|
||||||
|
brew services start tinyproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
#/bin/bash
|
||||||
|
# stop-tinyproxy.sh
|
||||||
|
brew services stop tinyproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.define "ubuntu" do |ubuntu|
|
||||||
|
ubuntu.vm.box = "ubuntu"
|
||||||
|
|
||||||
|
ubuntu.trigger.before :up do |trigger|
|
||||||
|
trigger.info = "Starting tinyproxy..."
|
||||||
|
trigger.run = {path: "start-tinyproxy.sh"}
|
||||||
|
end
|
||||||
|
|
||||||
|
ubuntu.trigger.after :destroy, :halt do |trigger|
|
||||||
|
trigger.info = "Stopping tinyproxy..."
|
||||||
|
trigger.run = {path: "stop-tinyproxy.sh"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `vagrant up` would fire the before trigger to start tinyproxy, where as
|
||||||
|
running either `vagrant destroy` or `vagrant halt` would stop tinyproxy.
|
|
@ -207,6 +207,14 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("triggers") %>>
|
||||||
|
<a href="/docs/triggers/">Triggers</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("triggers-configuration") %>><a href="/docs/triggers/configuration.html">Configuration</a></li>
|
||||||
|
<li<%= sidebar_current("triggers-usage") %>><a href="/docs/triggers/usage.html">Usage</a></li>
|
||||||
|
</ul>
|
||||||
|
</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