We had some cases where calling a capability that returned a boolean was not getting correctly unpacked, so instead of `true` or `false` the capability was putting out `VagrantPlugins::CommandServe::Type::Boolean`. This may have been happening in _all_ cases where a boolean was returned from a capability and we just didn't notice it yet because the return value was always truthy. These tweaks should help ensure that Ruby types make it out where they are supposed to be in Args::Direct usage.
648 lines
15 KiB
Ruby
648 lines
15 KiB
Ruby
# Patch things to produce proto messages
|
|
require "pathname"
|
|
require "securerandom"
|
|
require "google/protobuf/wrappers_pb"
|
|
require "google/protobuf/well_known_types"
|
|
|
|
PROTO_LOGGER = Log4r::Logger.new("vagrant::protologger")
|
|
|
|
# Default proto mapping
|
|
class Object
|
|
def self.to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Class.new(name: name)
|
|
end
|
|
|
|
def to_any
|
|
pro = to_proto
|
|
begin
|
|
Google::Protobuf::Any.pack(pro)
|
|
rescue
|
|
PROTO_LOGGER.warn("failed to any this type: #{self.class} value: #{self}")
|
|
raise
|
|
end
|
|
end
|
|
|
|
def to_proto
|
|
begin
|
|
klass = self.class.to_proto
|
|
data = Hash.new.tap do |h|
|
|
instance_variables.each do |v|
|
|
h[v.to_s.sub('@', '')] = instance_variable_get(v)
|
|
end
|
|
end
|
|
|
|
entries = data.map do |k, v|
|
|
Hashicorp::Vagrant::Sdk::Args::HashEntry.new(
|
|
key: k.to_any,
|
|
value: v.to_any,
|
|
)
|
|
end.compact
|
|
Hashicorp::Vagrant::Sdk::Config::RawRubyValue.new(
|
|
source: klass,
|
|
data: Hashicorp::Vagrant::Sdk::Args::Hash.new(entries: entries)
|
|
)
|
|
end
|
|
rescue => err
|
|
PROTO_LOGGER.warn("failed to proto #{self.class} | reason: #{err}")
|
|
raise
|
|
end
|
|
end
|
|
|
|
# Base types
|
|
class Array
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Array.new(
|
|
list: map(&:to_any)
|
|
)
|
|
end
|
|
end
|
|
|
|
class FalseClass
|
|
def to_proto
|
|
Google::Protobuf::BoolValue.new(value: false)
|
|
end
|
|
end
|
|
|
|
class Float
|
|
def to_proto
|
|
Google::Protobuf::FloatValue.new(value: self)
|
|
end
|
|
end
|
|
|
|
class Hash
|
|
def to_proto
|
|
entries = map do |k, v|
|
|
Hashicorp::Vagrant::Sdk::Args::HashEntry.new(
|
|
key: k.to_any,
|
|
value: v.to_any,
|
|
)
|
|
end
|
|
Hashicorp::Vagrant::Sdk::Args::Hash.new(entries: entries)
|
|
end
|
|
end
|
|
|
|
class Integer
|
|
def to_proto
|
|
Google::Protobuf::Int64Value.new(value: self)
|
|
end
|
|
end
|
|
|
|
class NilClass
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Null.new
|
|
end
|
|
end
|
|
|
|
class Pathname
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Path.new(
|
|
path: to_s
|
|
)
|
|
end
|
|
end
|
|
|
|
class Proc
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::ProcRef.new(
|
|
id: VagrantPlugins::CommandServe::Mappers::ProcRegistry.instance.register(self)
|
|
)
|
|
end
|
|
end
|
|
|
|
class Range
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Range.new(start: first, end: last)
|
|
end
|
|
end
|
|
|
|
class Set
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Set.new(list: to_a.to_proto)
|
|
end
|
|
end
|
|
|
|
class String
|
|
def to_proto
|
|
Google::Protobuf::StringValue.new(value: to_s)
|
|
end
|
|
end
|
|
|
|
class Symbol
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Symbol.new(str: to_s)
|
|
end
|
|
end
|
|
|
|
class TrueClass
|
|
def to_proto
|
|
Google::Protobuf::BoolValue.new(value: true)
|
|
end
|
|
end
|
|
|
|
# Complex types
|
|
class Vagrant::Plugin::V2::Config
|
|
def to_proto
|
|
data = Hash.new.tap do |h|
|
|
instance_variables.each do |v|
|
|
h[v.to_s.sub('@', '')] = instance_variable_get(v)
|
|
end
|
|
end
|
|
|
|
# Include a unique identifier for this configuration instance. This
|
|
# will allow us to identifier it later when it is decoded.
|
|
if !data.key?("_vagrant_config_identifier")
|
|
data["_vagrant_config_identifier"] = SecureRandom.uuid
|
|
end
|
|
|
|
entries = data.map do |k, v|
|
|
Hashicorp::Vagrant::Sdk::Args::HashEntry.new(
|
|
key: k.to_any,
|
|
value: v.to_any,
|
|
)
|
|
end
|
|
Hashicorp::Vagrant::Sdk::Args::ConfigData.new(
|
|
data: Hashicorp::Vagrant::Sdk::Args::Hash.new(entries: entries),
|
|
source: self.class.to_proto,
|
|
)
|
|
end
|
|
end
|
|
|
|
class Vagrant::Config::V2::Root
|
|
def to_proto
|
|
__internal_state["keys"].to_proto
|
|
end
|
|
end
|
|
|
|
class VagrantPlugins::CommandServe::Type::CommunicatorCommandArguments
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Communicator::Command.new(command: value)
|
|
end
|
|
end
|
|
|
|
class VagrantPlugins::CommandServe::Type::CommandInfo
|
|
def to_proto
|
|
flags = info.flags.map do |f|
|
|
Vagrant::Hashicorp::Sdk::Command::Flag.new(
|
|
long_name: f.long_name,
|
|
short_name: f.short_name,
|
|
description: f.description,
|
|
default_value: f.default_value,
|
|
type: f.type == :BOOL ? Vagrant::Hashicorp::Sdk::Command::Flag::Type::BOOL :
|
|
Hashicorp::Vagrant::Sdk::Command::Flag::Type::STRING
|
|
)
|
|
end
|
|
subcommands = info.subcommands.map do |s_info|
|
|
converter(s_info)
|
|
end
|
|
Hashicorp::Vagrant::Sdk::Command::CommandInfo.new(
|
|
name: info.name,
|
|
help: info.help,
|
|
synopsis: info.synopsis,
|
|
flags: flags,
|
|
subcommands: subcommands,
|
|
)
|
|
end
|
|
end
|
|
|
|
class VagrantPlugins::CommandServe::Type::Direct
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Direct.new(
|
|
arguments: value.to_proto
|
|
)
|
|
end
|
|
end
|
|
|
|
class VagrantPlugins::CommandServe::Type::Duration
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Duration.new(
|
|
duration: value
|
|
)
|
|
end
|
|
end
|
|
|
|
class VagrantPlugins::CommandServe::Type::Folders
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Folders.new(
|
|
folders: value.to_proto
|
|
)
|
|
end
|
|
end
|
|
|
|
class VagrantPlugins::CommandServe::Type::Options
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::Options.new(
|
|
options: value.to_proto
|
|
)
|
|
end
|
|
end
|
|
|
|
class Log4r::Logger
|
|
def to_proto
|
|
Hashicorp::Vagrant::Sdk::Args::RubyLogger.new(name: fullname)
|
|
end
|
|
end
|
|
|
|
# Proto conversions
|
|
class Google::Protobuf::Any
|
|
def to_ruby
|
|
_vagrant_unany(self).to_ruby
|
|
end
|
|
end
|
|
|
|
module Google::Protobuf::MessageExts
|
|
def to_ruby
|
|
return value if self.respond_to?(:value)
|
|
|
|
raise NotImplementedError,
|
|
"#{self.class}#to_ruby has not been implemented"
|
|
end
|
|
|
|
# Convert Any proto message to actual message type
|
|
#
|
|
# @param any [Google::Protobuf::Any]
|
|
# @return [Google::Protobuf::MessageExts]
|
|
def _vagrant_unany(any)
|
|
type = _vagrant_find_type(any.type_name.split("/").last.to_s)
|
|
any.unpack(type)
|
|
end
|
|
|
|
# Get const from name
|
|
#
|
|
# @param name [String]
|
|
# @return [Class]
|
|
def _vagrant_find_type(name)
|
|
name.to_s.split(".").inject(Object) { |memo, n|
|
|
c = memo.constants.detect { |mc| mc.to_s.downcase == n.to_s.downcase }
|
|
raise NameError,
|
|
"Failed to find constant for `#{name}'" if c.nil?
|
|
memo.const_get(c)
|
|
}
|
|
end
|
|
|
|
def _vagrant_load_client(klass)
|
|
raise TypeError,
|
|
"Proto is not a valid client message type (#{self.class})" if
|
|
!respond_to?(:addr)
|
|
|
|
cid = "client+" + addr.to_s
|
|
return VagrantPlugins::CommandServe.cache.get(cid) if
|
|
VagrantPlugins::CommandServe.cache.registered?(cid)
|
|
|
|
v = klass.load(self, broker: VagrantPlugins::CommandServe.broker)
|
|
VagrantPlugins::CommandServe.cache.register(cid, v)
|
|
v
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Array
|
|
def to_ruby
|
|
list.map { |a|
|
|
val = _vagrant_unany(a).to_ruby
|
|
val.is_a?(VagrantPlugins::CommandServe::Type) ? val.value : val
|
|
}
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::BoxMetadata
|
|
# TODO(spox): should this be returning a box metadata instance instead of client?
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::BoxMetadata)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Class
|
|
def to_ruby
|
|
if name.to_s.empty?
|
|
raise NameError,
|
|
"No name defined for for class"
|
|
end
|
|
name.to_s.split("::").inject(Object) { |memo, n|
|
|
c = memo.constants.detect { |mc| mc.to_s.downcase == n.to_s.downcase }
|
|
raise NameError,
|
|
"Failed to find constant for `#{name}'" if c.nil?
|
|
memo.const_get(c)
|
|
}
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::CorePluginManager
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::CorePluginManager)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::ConfigData
|
|
def to_ruby
|
|
base_klass = source.to_ruby
|
|
if [0, -1].include?(base_klass.instance_method(:initialize).arity)
|
|
klass = base_klass
|
|
else
|
|
klass = Class.new(base_klass)
|
|
klass.class_eval("
|
|
def self.to_proto
|
|
#{base_klass.name}.to_proto
|
|
end
|
|
def self.class
|
|
#{base_klass.name}
|
|
end
|
|
def initialize
|
|
end
|
|
")
|
|
end
|
|
instance = klass.new
|
|
d = data.to_ruby
|
|
|
|
# Since we are restoring the config, if the config this
|
|
# represents was already finalized we finalize it first
|
|
# before we inject the instance variables to get as close
|
|
# to the correct original state as possible
|
|
if d.key?("__finalized")
|
|
instance.finalize!
|
|
instance._finalize!
|
|
end
|
|
|
|
# Now set our data into the instance
|
|
d.each_pair do |k, v|
|
|
instance.instance_variable_set("@#{k}", v)
|
|
end
|
|
|
|
instance
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Direct
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Type::Direct.new(
|
|
arguments: arguments.map { |arg|
|
|
val = arg.to_ruby
|
|
val.is_a?(VagrantPlugins::CommandServe::Type) ? val.value : val
|
|
}
|
|
)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Folders
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Type::Folders.new(value: folders.to_ruby)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Guest
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::Guest)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Hash
|
|
def to_ruby
|
|
data = Hash.new
|
|
entries.each do |e|
|
|
key = _vagrant_unany(e.key).to_ruby
|
|
value = _vagrant_unany(e.value).to_ruby
|
|
data[key] = value.is_a?(VagrantPlugins::CommandServe::Type) ? value.value : value
|
|
end
|
|
|
|
data
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Host
|
|
def to_ruby
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::Host)
|
|
Vagrant::Host.new(client, nil, Vagrant.plugin("2").local_manager.host_capabilities)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::NamedCapability
|
|
def to_ruby
|
|
capability.to_s.to_sym
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Null
|
|
def to_ruby
|
|
nil
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Options
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Type::Options.new(value: options.to_ruby)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Path
|
|
def to_ruby
|
|
Pathname.new(path.to_s)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::ProcRef
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Mappers::ProcRegistry.instance.fetch(id)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Project
|
|
def to_ruby
|
|
cid = "environment"+addr.to_s
|
|
return VagrantPlugins::CommandServe.cache.get(cid) if
|
|
VagrantPlugins::CommandServe.cache.registered?(cid)
|
|
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::Project)
|
|
ui = client.ui.to_ui
|
|
|
|
env = Vagrant::Environment.new(client: client, ui: ui)
|
|
VagrantPlugins::CommandServe.cache.register(cid, env)
|
|
env
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Provisioner
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::Provisioner)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Provider
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::Provider)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Range
|
|
def to_ruby
|
|
Range.new(self.start, self.end)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Config::RawRubyValue
|
|
def to_ruby
|
|
base_klass = source.to_ruby
|
|
if [0, -1].include?(base_klass.instance_method(:initialize).arity)
|
|
klass = base_klass
|
|
else
|
|
klass = Class.new(base_klass)
|
|
klass.class_eval("
|
|
def self.class
|
|
#{base_klass.name}
|
|
end
|
|
def initialize
|
|
end
|
|
")
|
|
end
|
|
|
|
instance = klass.new
|
|
d = data.to_ruby
|
|
|
|
d.each_pair do |k, v|
|
|
instance.instance_variable_set("@#{k}", v)
|
|
end
|
|
|
|
instance
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::RubyLogger
|
|
def to_ruby
|
|
Log4r::Logger.new(name)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Set
|
|
def to_ruby
|
|
::Set.new(list.to_ruby)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::StateBag
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::StateBag)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::SyncedFolder
|
|
def to_ruby
|
|
cid = "syncedfolder+" + addr.to_s
|
|
return VagrantPlugins::CommandServe.cache.get(cid) if
|
|
VagrantPlugins::CommandServe.cache.registered?(cid)
|
|
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::SyncedFolder)
|
|
fld = Vagrant::Plugin::Remote::SyncedFolder.new(client: client)
|
|
VagrantPlugins::CommandServe.cache.register(cid, fld)
|
|
fld
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Symbol
|
|
def to_ruby
|
|
str.to_sym
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Target
|
|
def to_ruby
|
|
cid = "machine+" + addr.to_s
|
|
return VagrantPlugins::CommandServe.cache.get(cid) if
|
|
VagrantPlugins::CommandServe.cache.registered?(cid)
|
|
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::Target)
|
|
env = client.project.to_ruby
|
|
machine = env.machine(client.name.to_sym, client.provider_name.to_sym)
|
|
VagrantPlugins::CommandServe.cache.register(cid, machine)
|
|
machine
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Target::Machine
|
|
def to_ruby
|
|
cid = "machine+" + addr.to_s
|
|
return VagrantPlugins::CommandServe.cache.get(cid) if
|
|
VagrantPlugins::CommandServe.cache.registered?(cid)
|
|
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::Target::Machine)
|
|
m = Vagrant::Machine.new(client: client)
|
|
VagrantPlugins::CommandServe.cache.register(cid, m)
|
|
m
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Target::Machine::State
|
|
def to_ruby
|
|
Vagrant::MachineState.new(
|
|
m.id.to_sym, m.short_description, m.long_description
|
|
)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::TargetIndex
|
|
def to_ruby
|
|
_vagrant_load_client(VagrantPlugins::CommandServe::Client::TargetIndex)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::TimeDuration
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Type::Duration.new(value: duration)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::TerminalUI
|
|
def to_ruby
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::Terminal)
|
|
Vagrant::UI::Remote.new(client)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Args::Vagrantfile
|
|
def to_ruby
|
|
client = _vagrant_load_client(VagrantPlugins::CommandServe::Client::Vagrantfile)
|
|
Vagrant::Vagrantfile.new(client: client)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Command::Arguments
|
|
def to_ruby
|
|
_args = args.to_a
|
|
_flags = Hash.new.tap do |flgs|
|
|
flags.each do |f|
|
|
if f.type == :BOOL
|
|
flgs[f.name] = f.bool
|
|
else
|
|
flgs[f.name] = f.string
|
|
end
|
|
end
|
|
end
|
|
VagrantPlugins::CommandServe::Type::CommandArguments.new(args: _args, flags: _flags)
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Command::CommandInfo
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Type::CommandInfo.new(
|
|
name: name,
|
|
help: help,
|
|
synopsis: synopsis,
|
|
).tap do |c|
|
|
flags.each do |f|
|
|
c.add_flag(
|
|
long_name: f.long_name,
|
|
short_name: f.short_name,
|
|
description: f.description,
|
|
default_value: f.default_value,
|
|
type: f.type,
|
|
)
|
|
end
|
|
subcommands.each do |s_proto|
|
|
c.add_subcommand(s_proto.to_ruby)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class Hashicorp::Vagrant::Sdk::Communicator::Command
|
|
def to_ruby
|
|
VagrantPlugins::CommandServe::Type::CommunicatorCommandArguments.new(value: command)
|
|
end
|
|
end
|