304 lines
11 KiB
Ruby
304 lines
11 KiB
Ruby
require "log4r"
|
|
|
|
module Vagrant
|
|
module Plugin
|
|
module Remote
|
|
# 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
|
|
class << self
|
|
# @return [VagrantPlugins::Command::Serve::Client::PluginManager] remote manager client
|
|
attr_accessor :client
|
|
|
|
# @return [VagrantPlugins::Command::Serve::Client::CorePluginManager] remote manager client for core plugins
|
|
attr_accessor :core_client
|
|
end
|
|
|
|
# This wrapper class is used for encapsulating a remote plugin class. This
|
|
# block is the content for the anonymous subclass created for the remote
|
|
# plugin. Its job is to store the name and type of a plugin within the
|
|
# class context. When initialized it will use the remote plugin manager
|
|
# to load the proper client into the remote plugin instance. It also
|
|
# handles passing non-API method calls to the local instance of a plugin
|
|
# if the plugin exists within the Ruby runtime.
|
|
WRAPPER_CLASS = proc do |klass|
|
|
class << klass
|
|
# @return [String] name of the plugin (virtualbox, smb, shell, etc.)
|
|
attr_accessor :plugin_name
|
|
# @return [String] type of plugin (Provider, Provisioner, etc.)
|
|
attr_accessor :type
|
|
|
|
# @return [String]
|
|
def name
|
|
"Vagrant::Plugin::Remote::#{type.to_s.split(/-_/).map(&:capitalize).join}"
|
|
end
|
|
|
|
# @return [String]
|
|
def to_s
|
|
"<#{name} plugin_name=#{plugin_name}>"
|
|
end
|
|
|
|
# @return [String]
|
|
def inspect
|
|
"<#{name} plugin_name=#{plugin_name} type=#{type}>"
|
|
end
|
|
|
|
def inherited(klass) # :nodoc:
|
|
|
|
klass.plugin_name = plugin_name
|
|
klass.type = type
|
|
end
|
|
|
|
# @return [VagrantPlugins::Commands::Serve::Client] client for plugin
|
|
def client
|
|
return @client if @client
|
|
@client = Manager.client.get_plugin(
|
|
name: plugin_name,
|
|
type: type
|
|
)
|
|
end
|
|
end
|
|
|
|
def initialize(*args, **kwargs, &block)
|
|
@logger = Log4r::Logger.new(self.class.name.downcase)
|
|
kwargs[:client] = self.class.client
|
|
super(*args, **kwargs, &block)
|
|
kwargs.delete(:client)
|
|
@init = [args, kwargs, block]
|
|
end
|
|
|
|
# @return [String] name of plugin
|
|
def name
|
|
self.class.plugin_name
|
|
end
|
|
|
|
# @return [String]
|
|
def inspect
|
|
"<#{self.class.name}:#{object_id} plugin_name=#{name} type=#{self.class.type}>"
|
|
end
|
|
|
|
# @return [String]
|
|
def to_s
|
|
"<#{self.class.name}:#{object_id}>"
|
|
end
|
|
|
|
# If an unknown method is called on the plugin, this will check if the
|
|
# actual plugin is local to the Ruby runtime. If it is not, a NoMethodError
|
|
# will be generated. If it is, the local plugin will either be loaded
|
|
# from the cache or instantiated and the method call will be executed
|
|
# against the local plugin instance.
|
|
def method_missing(*args, **kwargs, &block)
|
|
klass = get_local_plugin
|
|
return super if klass.nil?
|
|
@logger.debug("found local plugin class #{self.class.name} -> #{klass.name}")
|
|
c = VagrantPlugins::CommandServe.cache
|
|
key = c.key(klass, *@init[0])
|
|
if !c.registered?(key)
|
|
@logger.debug("creating new local plugin instance of #{klass} with args: #{@init}")
|
|
c.register(key, klass.new(*@init[0], **@init[1], &@init[2]))
|
|
end
|
|
@logger.debug("sending ##{args.first} result to local plugin #{klass}")
|
|
c.get(key).send(*args, **kwargs, &block)
|
|
end
|
|
|
|
private
|
|
|
|
# @return [Class, NilClass] class of the local plugin
|
|
def get_local_plugin
|
|
m = ["#{self.class.type.downcase}s",
|
|
"#{self.class.type.downcase}es"].detect { |i|
|
|
Vagrant.plugin("2").local_manager.respond_to?(i)
|
|
}
|
|
if m.nil?
|
|
@logger.debug("failed to locate valid local plugin registry method for plugin type #{self.class.type}")
|
|
return
|
|
end
|
|
klass = Array(Vagrant.plugin("2").local_manager.
|
|
send(m)[self.class.plugin_name.to_sym]).first
|
|
@logger.trace("local plugin lookup for #{self.class.name} / #{self.class.plugin_name} / #{self.class.type}: #{klass}")
|
|
klass
|
|
end
|
|
end
|
|
|
|
# @return [V2::Manager]
|
|
attr_reader :real_manager
|
|
|
|
# Create a new remote plugin manager
|
|
#
|
|
# @param manager [V2::Manger]
|
|
# @return [Remote::Manager]
|
|
def initialize(manager)
|
|
@logger = Log4r::Logger.new(self.class.name.downcase)
|
|
@real_manager = manager
|
|
end
|
|
|
|
def method_missing(m, *args, **kwargs, &block)
|
|
@logger.debug("method not defined, sending to real manager `#{m}'")
|
|
@real_manager.send(m, *args, **kwargs, &block)
|
|
end
|
|
|
|
# @return [VagrantPlugins::Command::Serve::Client::PluginManager] remote manager client
|
|
def plugin_manager
|
|
self.class.client
|
|
end
|
|
|
|
# @return [VagrantPlugins::Command::Serve::Client::CorePluginManager] remote core manager client
|
|
def core_plugin_manager
|
|
self.class.core_client
|
|
end
|
|
|
|
# Synced folder plugins are registered with an integer priority, but in
|
|
# remote mode this is all captured by InternalService#get_plugins and
|
|
# handled on the Go sidw. Within the remote manager we return a stub
|
|
# value to ensure that any callers get the same shape of return value
|
|
# from the registry and don't blow up.
|
|
SYNCED_FOLDERS_STUB_PRIORITY = 123
|
|
|
|
# This returns all synced folder implementations.
|
|
#
|
|
# @return [Registry]
|
|
def synced_folders
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:synced_folder).each do |plg|
|
|
sf_class = Class.new(Remote::SyncedFolder, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
[sf_class, SYNCED_FOLDERS_STUB_PRIORITY]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This returns all command implementations.
|
|
#
|
|
# @return [Registry]
|
|
def commands
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:command).each do |plg|
|
|
sf_class = Class.new(Remote::Command, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
[proc{sf_class}]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This returns all communicator implementations.
|
|
#
|
|
# @return [Registry]
|
|
def communicators
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:communicator).each do |plg|
|
|
sf_class = Class.new(Remote::Communicator, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
proc{sf_class}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# def config
|
|
# return real_manager.synced_folders if plugin_manager.nil?
|
|
|
|
# Registry.new.tap do |result|
|
|
# plugin_manager.list_plugins(:config).each do |plg|
|
|
# sf_class = Class.new(Remote::Config, &WRAPPER_CLASS)
|
|
# sf_class.plugin_name = plg[:name]
|
|
# sf_class.type = plg[:type]
|
|
# result.register(plg[:name].to_sym) do
|
|
# proc{sf_class}
|
|
# end
|
|
# end
|
|
# end
|
|
# end
|
|
|
|
# This returns all guest implementations.
|
|
#
|
|
# @return [Registry]
|
|
def guests
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:guest).each do |plg|
|
|
sf_class = Class.new(Remote::Guest, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
proc{sf_class}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This returns all host implementations.
|
|
#
|
|
# @return [Registry]
|
|
def hosts
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:host).each do |plg|
|
|
sf_class = Class.new(Remote::Host, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
proc{sf_class}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This returns all provider implementations.
|
|
#
|
|
# @return [Registry]
|
|
def providers
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:provider).each do |plg|
|
|
sf_class = Class.new(Remote::Provider, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
# TODO: Options hash should be what?
|
|
[sf_class, {}]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def provisioners
|
|
return real_manager.provisioners if plugin_manager.nil?
|
|
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:provisioner).each do |plg|
|
|
sf_class = Class.new(Remote::Provisioner, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
sf_class
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This returns all push implementations.
|
|
#
|
|
# @return [Registry]
|
|
def pushes
|
|
Registry.new.tap do |result|
|
|
plugin_manager.list_plugins(:push).each do |plg|
|
|
sf_class = Class.new(Remote::Push, &WRAPPER_CLASS)
|
|
sf_class.plugin_name = plg[:name]
|
|
sf_class.type = plg[:type]
|
|
result.register(plg[:name].to_sym) do
|
|
sf_class
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|