diff --git a/plugins/commands/serve/service.rb b/plugins/commands/serve/service.rb index 272780085..385a1f058 100644 --- a/plugins/commands/serve/service.rb +++ b/plugins/commands/serve/service.rb @@ -1,6 +1,7 @@ module VagrantPlugins module CommandServe module Service + autoload :CapabilityPlatformService, Vagrant.source_root.join("plugins/commands/serve/service/capability_platform_service").to_s autoload :CommandService, Vagrant.source_root.join("plugins/commands/serve/service/command_service").to_s autoload :GuestService, Vagrant.source_root.join("plugins/commands/serve/service/guest_service").to_s autoload :HostService, Vagrant.source_root.join("plugins/commands/serve/service/host_service").to_s diff --git a/plugins/commands/serve/service/capability_platform_service.rb b/plugins/commands/serve/service/capability_platform_service.rb new file mode 100644 index 000000000..8f40e88f5 --- /dev/null +++ b/plugins/commands/serve/service/capability_platform_service.rb @@ -0,0 +1,110 @@ +require "google/protobuf/well_known_types" + +module VagrantPlugins + module CommandServe + module Service + module CapabilityPlatformService + prepend Util::HasMapper + prepend Util::HasBroker + prepend Util::ExceptionLogger + + def initialize_capability_platform!(capabilities, default_args) + @capabilities = capabilities + @default_args = default_args + end + + + def has_capability_spec(*_) + SDK::FuncSpec.new( + name: "has_capability_spec", + args: [ + SDK::FuncSpec::Value.new( + type: "hashicorp.vagrant.sdk.Args.NamedCapability", + name: "", + ) + ], + result: [ + SDK::FuncSpec::Value.new( + type: "hashicorp.vagrant.sdk.Platform.Capability.CheckResp", + name: "", + ), + ], + ) + end + + def has_capability(req, ctx) + ServiceInfo.with_info(ctx) do |info| + cap_name = req.args.detect { |a| + a.type == "hashicorp.vagrant.sdk.Args.NamedCapability" + }&.value&.value.strip.gsub("\b", "") + plugin_name = info.plugin_name + LOGGER.debug("checking for #{cap_name} capability in #{plugin_name}") + + caps_registry = @capabilities[plugin_name.to_sym] + has_cap = caps_registry.key?(cap_name.to_sym) + + SDK::Platform::Capability::CheckResp.new( + has_capability: has_cap + ) + end + end + + def capability_spec(req, ctx) + ServiceInfo.with_info(ctx) do |info| + cap_name = req.name.to_sym + plugin_name = info.plugin_name.to_sym + LOGGER.debug("generating capabillity spec for #{cap_name} capability in #{plugin_name}") + caps_registry = @capabilities[plugin_name] + + target_cap = caps_registry.get(cap_name) + args = target_cap.method(cap_name).parameters + # The first argument is always a machine, drop it + args.shift + + cap_args = default_args + + # TODO: take the rest of `args` and create entries for them in + # `cap_args` + + return SDK::FuncSpec.new( + name: "has_capability_spec", + args: cap_args, + result: [ + SDK::FuncSpec::Value.new( + type: "hashicorp.vagrant.sdk.Platform.Capability.Resp", + name: "", + ), + ], + ) + end + end + + def capability(req, ctx) + ServiceInfo.with_info(ctx) do |info| + LOGGER.debug("executing capability, got req #{req}") + cap_name = req.name.to_sym + plugin_name = info.plugin_name.to_sym + caps_registry = @capabilities[plugin_name] + target_cap = caps_registry.get(cap_name) + + # TODO: how to get this Target out of the args + + # A machine should always be provided to a guest capability + raw_target = req.func_args.args.detect { |a| + a.type == "hashicorp.vagrant.sdk.Args.Target" + }&.value&.value + target = Client::Target.load(raw_target, broker: broker) + project = target.project + env = Vagrant::Environment.new({client: project}) + machine = env.machine(target.name.to_sym, target.provider_name.to_sym) + + cap_method = target_cap.method(cap_name) + + # TODO: pass in other args too + cap_method.call(machine) + end + end + end + end + end +end diff --git a/plugins/commands/serve/service/guest_service.rb b/plugins/commands/serve/service/guest_service.rb index 19ca409bf..6cf962a91 100644 --- a/plugins/commands/serve/service/guest_service.rb +++ b/plugins/commands/serve/service/guest_service.rb @@ -4,13 +4,25 @@ module VagrantPlugins module CommandServe module Service class GuestService < Hashicorp::Vagrant::Sdk::GuestService::Service + + include CapabilityPlatformService + prepend Util::HasMapper prepend Util::HasBroker prepend Util::ExceptionLogger LOGGER = Log4r::Logger.new("vagrant::command::serve::guest") def initialize(*args, **opts, &block) - super() + caps = Vagrant.plugin("2").manager.guest_capabilities + default_args = [ + # Always get a target to pass the guest capability + SDK::FuncSpec::Value.new( + type: "hashicorp.vagrant.sdk.Args.Target", + name: "", + ), + ] + initialize_capability_platform!(caps, default_args) + super(*args, **opts, &block) end def detect_spec(*_) @@ -79,100 +91,6 @@ module VagrantPlugins ) end end - - def has_capability_spec(*_) - SDK::FuncSpec.new( - name: "has_capability_spec", - args: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Args.NamedCapability", - name: "", - ) - ], - result: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Platform.Capability.CheckResp", - name: "", - ), - ], - ) - end - - def has_capability(req, ctx) - ServiceInfo.with_info(ctx) do |info| - cap_name = mapper.funcspec_map(req).to_sym - - plugin_name = info.plugin_name - LOGGER.debug("checking for #{cap_name} capability in #{plugin_name}") - - caps_registry = Vagrant.plugin("2").manager.guest_capabilities[plugin_name.to_sym] - has_cap = caps_registry.key?(cap_name.to_sym) - - SDK::Platform::Capability::CheckResp.new( - has_capability: has_cap - ) - end - end - - def capability_spec(req, ctx) - ServiceInfo.with_info(ctx) do |info| - cap_name = req.name.to_sym - plugin_name = info.plugin_name.to_sym - LOGGER.debug("generating capabillity spec for #{cap_name} capability in #{plugin_name}") - caps_registry = Vagrant.plugin("2").manager.guest_capabilities[plugin_name] - - target_cap = caps_registry.get(cap_name) - args = target_cap.method(cap_name).parameters - # The first argument is always a machine, drop it - args.shift - - cap_args = [ - # Always get a target to pass the guest capability - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Args.Target", - name: "", - ), - ] - - # TODO: take the rest of `args` and create entries for them in - # `cap_args` - - return SDK::FuncSpec.new( - name: "has_capability_spec", - args: cap_args, - result: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Platform.Capability.Resp", - name: "", - ), - ], - ) - end - end - - def capability(req, ctx) - ServiceInfo.with_info(ctx) do |info| - LOGGER.debug("executing capability, got req #{req}") - cap_name = req.name.to_sym - plugin_name = info.plugin_name.to_sym - caps_registry = Vagrant.plugin("2").manager.guest_capabilities[plugin_name] - target_cap = caps_registry.get(cap_name) - - # A machine should always be provided to a guest capability - raw_target = req.func_args.args.detect { |a| - a.type == "hashicorp.vagrant.sdk.Args.Target" - }&.value&.value - target = Client::Target.load(raw_target, broker: broker) - project = target.project - env = Vagrant::Environment.new({client: project}) - machine = env.machine(target.name.to_sym, target.provider_name.to_sym) - - cap_method = target_cap.method(cap_name) - - # TODO: pass in other args too - cap_method.call(machine) - end - end end end end diff --git a/plugins/commands/serve/service/host_service.rb b/plugins/commands/serve/service/host_service.rb index 7c533614f..01792a5ca 100644 --- a/plugins/commands/serve/service/host_service.rb +++ b/plugins/commands/serve/service/host_service.rb @@ -4,10 +4,26 @@ module VagrantPlugins module CommandServe module Service class HostService < Hashicorp::Vagrant::Sdk::HostService::Service + + include CapabilityPlatformService + prepend Util::HasMapper prepend Util::HasBroker prepend Util::ExceptionLogger + def initialize(*args, **opts, &block) + caps = Vagrant.plugin("2").manager.host_capabilities + default_args = [ + # Always get a target to pass the guest capability + SDK::FuncSpec::Value.new( + type: "hashicorp.vagrant.sdk.Args.Project", + name: "", + ), + ] + initialize_capability_platform!(caps, default_args) + super(*args, **opts, &block) + end + def detect_spec(*_) SDK::FuncSpec.new( name: "detect_spec", @@ -54,88 +70,6 @@ module VagrantPlugins ) end end - - def has_capability_spec(*_) - SDK::FuncSpec.new( - name: "has_capability_spec", - args: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Args.NamedCapability", - name: "", - ) - ], - result: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Host.Capability.CheckResp", - name: "", - ), - ], - ) - end - - def has_capability(req, ctx) - ServiceInfo.with_info(ctx) do |info| - plugin_name = info.plugin_name - n_cap = req.args.detect { |a| - a.type == 'hashicorp.vagrant.sdk.Args.NamedCapability' - }&.value&.value - p = Vagrant::Host.new( - plugin_name.to_sym, - Vagrant.plugin("2").manager.hosts, - Vagrant.plugin("2").manager.host_capabilities, - nil, - ) - SDK::Host::Capability::CheckResp.new( - has_capability: p.capability?(n_cap.strip.to_s.to_sym) - ) - end - end - - def capability_spec(*_) - SDK::FuncSpec.new( - name: "capability_spec", - args: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Args.TerminalUI", - name: "", - ), - ], - result: [ - SDK::FuncSpec::Value.new( - type: "hashicorp.vagrant.sdk.Host.Capability.Resp" - ) - ] - ) - end - - def capability(req, ctx) - ServiceInfo.with_info(ctx) do |info| - begin - res = nil - plugin_name = info.plugin_name - n_cap = req.name - - ui_client = mapper.funcspec_map(req.spec) - ui = Vagrant::UI::Remote.new(ui_client) - - p = Vagrant::Host.new( - plugin_name.to_sym, - Vagrant.plugin("2").manager.hosts, - Vagrant.plugin("2").manager.host_capabilities, - nil, - ) - res = p.capability(n_cap.to_s.strip.to_sym, ui) # TODO(spox): first arg needs to be env / statebag - vres = Google::Protobuf::Value.new - vres.from_ruby(res) - - SDK::Host::Capability::Resp.new( - result: Google::Protobuf::Any.pack(vres) - ) - rescue => err - raise "#{err.class}: #{err}\n#{err.backtrace.join("\n")}" - end - end - end end end end