Relying on HashWithIndifferentAccess in Hash mappers results in errors when hashes get merged in Ruby. When merges between regular hashes and HashWithIndifferentAccess happens, then all the keys from HashWithIndifferentAccess are transformed to strings.
645 lines
15 KiB
Ruby
645 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(&:to_ruby)
|
|
)
|
|
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
|