Chris Roberts d08c68ecf3 Adjust how trigger actions are inserted into the stack
This adjusts how triggers are implemented during a normal run. Any
defined triggers which are applicable are located and injected into
the run stack as the stack is built, including hook type triggers.

Support is included for dynamic hook lookup.

The data type used when defining triggers has also been relaxed to
support symbols, strings, or constants.
2020-03-17 15:07:36 -07:00

290 lines
8.4 KiB
Ruby

require "log4r"
module Vagrant
module Plugin
module V2
# This class maintains a list of all the registered plugins as well
# as provides methods that allow querying all registered components of
# those plugins as a single unit.
class Manager
attr_reader :registered
def initialize
@logger = Log4r::Logger.new("vagrant::plugin::v2::manager")
@registered = []
end
# This returns all the action hooks.
#
# @return [Array]
def action_hooks(hook_name)
result = []
@registered.each do |plugin|
result += plugin.components.action_hooks[Plugin::ALL_ACTIONS]
result += plugin.components.action_hooks[hook_name]
end
result
end
# Find all hooks that are applicable for the given key. This
# lookup does not include hooks which are defined for ALL_ACTIONS.
# Key lookups will match on either string or symbol values. The
# provided keys is broken down into multiple parts for lookups,
# which allows defining hooks with an entire namespaced name,
# or a short suffx. For example:
#
# Assume we are given an action class
# key = Vagrant::Action::Builtin::SyncedFolders
#
# The list of keys that will be checked for hooks:
# ["Vagrant::Action::Builtin::SyncedFolders", "vagrant_action_builtin_synced_folders",
# "Action::Builtin::SyncedFolders", "action_builtin_synced_folders",
# "Builtin::SyncedFolders", "builtin_synced_folders",
# "SyncedFolders", "synced_folders"]
#
# @param key [Class, String] key Key for hook lookups
# @return [Array<Proc>]
def find_action_hooks(key)
result = []
generate_hook_keys(key).each do |k|
@registered.each do |plugin|
result += plugin.components.action_hooks[k]
result += plugin.components.action_hooks[k.to_sym]
end
end
result
end
# Generate all valid lookup keys for given key
#
# @param [Class, String] key Base key for generation
# @return [Array<String>] all valid keys
def generate_hook_keys(key)
if key.is_a?(Class)
key = key.name.to_s
else
key = key.to_s
end
parts = key.split("::")
[].tap do |keys|
until parts.empty?
x = parts.join("::")
keys << x
y = x.gsub(/([a-z])([A-Z])/, '\1_\2').gsub('::', '_').downcase
keys << y if x != y
parts.shift
end
end
end
# This returns all the registered commands.
#
# @return [Registry<Symbol, Array<Proc, Hash>>]
def commands
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.commands)
end
end
end
# This returns all the registered communicators.
#
# @return [Hash]
def communicators
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.communicator)
end
end
end
# This returns all the registered configuration classes.
#
# @return [Hash]
def config
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.configs[:top])
end
end
end
# This returns all the registered guests.
#
# @return [Hash]
def guests
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.guests)
end
end
end
# This returns all the registered guest capabilities.
#
# @return [Hash]
def guest_capabilities
results = Hash.new { |h, k| h[k] = Registry.new }
@registered.each do |plugin|
plugin.components.guest_capabilities.each do |guest, caps|
results[guest].merge!(caps)
end
end
results
end
# This returns all the registered guests.
#
# @return [Hash]
def hosts
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.hosts)
end
end
end
# This returns all the registered host capabilities.
#
# @return [Hash]
def host_capabilities
results = Hash.new { |h, k| h[k] = Registry.new }
@registered.each do |plugin|
plugin.components.host_capabilities.each do |host, caps|
results[host].merge!(caps)
end
end
results
end
# This returns all registered providers.
#
# @return [Hash]
def providers
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.providers)
end
end
end
# This returns all the registered provider capabilities.
#
# @return [Hash]
def provider_capabilities
results = Hash.new { |h, k| h[k] = Registry.new }
@registered.each do |plugin|
plugin.components.provider_capabilities.each do |provider, caps|
results[provider].merge!(caps)
end
end
results
end
# This returns all the config classes for the various providers.
#
# @return [Hash]
def provider_configs
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.configs[:provider])
end
end
end
# This returns all the config classes for the various provisioners.
#
# @return [Registry]
def provisioner_configs
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.configs[:provisioner])
end
end
end
# This returns all registered provisioners.
#
# @return [Hash]
def provisioners
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.provisioner)
end
end
end
# This returns all registered pushes.
#
# @return [Registry]
def pushes
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.pushes)
end
end
end
# This returns all the config classes for the various pushes.
#
# @return [Registry]
def push_configs
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.configs[:push])
end
end
end
# This returns all synced folder implementations.
#
# @return [Registry]
def synced_folders
Registry.new.tap do |result|
@registered.each do |plugin|
result.merge!(plugin.components.synced_folders)
end
end
end
# This registers a plugin. This should _NEVER_ be called by the public
# and should only be called from within Vagrant. Vagrant will
# automatically register V2 plugins when a name is set on the
# plugin.
def register(plugin)
if !@registered.include?(plugin)
@logger.info("Registered plugin: #{plugin.name}")
@registered << plugin
end
end
# This clears out all the registered plugins. This is only used by
# unit tests and should not be called directly.
def reset!
@registered.clear
end
# This unregisters a plugin so that its components will no longer
# be used. Note that this should only be used for testing purposes.
def unregister(plugin)
if @registered.include?(plugin)
@logger.info("Unregistered: #{plugin.name}")
@registered.delete(plugin)
end
end
end
end
end
end