diff --git a/lib/vagrant/plugin/remote/manager.rb b/lib/vagrant/plugin/remote/manager.rb index 918f0e528..4751ba5ad 100644 --- a/lib/vagrant/plugin/remote/manager.rb +++ b/lib/vagrant/plugin/remote/manager.rb @@ -8,29 +8,46 @@ module Vagrant # those plugins as a single unit. class Manager class << self + # @return [VagrantPlugins::Command::Serve::Client::PluginManager] remote manager client attr_accessor :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 - attr_accessor :plugin_name, :type + # @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) + 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( @@ -41,25 +58,73 @@ module Vagrant 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::Service.cache + key = c.key(klass, *args) + 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 @@ -70,6 +135,7 @@ module Vagrant @real_manager.send(m, *args, **kwargs, &block) end + # @return [VagrantPlugins::Command::Serve::Client::PluginManager] remote manager client def plugin_manager self.class.client end @@ -85,8 +151,6 @@ module Vagrant # # @return [Registry] def synced_folders - return real_manager.synced_folders if plugin_manager.nil? - Registry.new.tap do |result| plugin_manager.list_plugins(:synced_folder).each do |plg| sf_class = Class.new(Remote::SyncedFolder, &WRAPPER_CLASS) @@ -99,36 +163,38 @@ module Vagrant end end - def commands - return real_manager.synced_folders if plugin_manager.nil? - - 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}, {}] # TODO(spox): Options hash should be what? - end - end - end - end - - # def communicators - # return real_manager.synced_folders if plugin_manager.nil? - + # This returns all command implementations. + # + # @return [Registry] + # def commands # Registry.new.tap do |result| - # plugin_manager.list_plugins(:communicator).each do |plg| - # sf_class = Class.new(Remote::Communicator, &WRAPPER_CLASS) + # 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} + # [proc{sf_class}, {}] # TODO(spox): Options hash should be what? # 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? @@ -144,9 +210,10 @@ module Vagrant # end # end + # This returns all guest implementations. + # + # @return [Registry] def guests - return real_manager.synced_folders if plugin_manager.nil? - Registry.new.tap do |result| plugin_manager.list_plugins(:guest).each do |plg| sf_class = Class.new(Remote::Guest, &WRAPPER_CLASS) @@ -159,9 +226,10 @@ module Vagrant end end + # This returns all host implementations. + # + # @return [Registry] def hosts - return real_manager.synced_folders if plugin_manager.nil? - Registry.new.tap do |result| plugin_manager.list_plugins(:host).each do |plg| sf_class = Class.new(Remote::Host, &WRAPPER_CLASS) @@ -174,9 +242,10 @@ module Vagrant end end + # This returns all provider implementations. + # + # @return [Registry] def providers - return real_manager.providers if plugin_manager.nil? - Registry.new.tap do |result| plugin_manager.list_plugins(:provider).each do |plg| sf_class = Class.new(Remote::Provider, &WRAPPER_CLASS) @@ -205,9 +274,10 @@ module Vagrant end end + # This returns all push implementations. + # + # @return [Registry] def pushes - return real_manager.pushes if plugin_manager.nil? - Registry.new.tap do |result| plugin_manager.list_plugins(:push).each do |plg| sf_class = Class.new(Remote::Push, &WRAPPER_CLASS)