Chris Roberts e958c6183a Adds initial HCP config support
Adds initial basic support for HCP based configuration in vagrant-go.
The initalization process has been updated to remove Vagrantfile parsing
from the client, moving it to the runner using init jobs for the basis
and the project (if there is one). Detection is done on the file based
on extension for Ruby based parsing or HCP based parsing.

Current HCP parsing is extremely simple and currently just a base to
build off. Config components will be able to implement an `Init`
function to handle receiving configuration data from a non-native source
file. This will be extended to include a default approach for injecting
defined data in the future.

Some cleanup was done in the state around validations. Some logging
adjustments were applied on the Ruby side for better behavior
consistency.

VirtualBox provider now caches locale detection to prevent multiple
checks every time the driver is initialized.
2023-09-07 17:26:10 -07:00

247 lines
6.5 KiB
Ruby

# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
module VagrantPlugins
module CommandServe
class Mappers
class ConfigDataFromSpec < Mapper
def initialize
super(
inputs: [Input.new(type: SDK::FuncSpec::Value) { |arg|
arg.type == "hashicorp.vagrant.sdk.Args.ConfigData" &&
!arg&.value&.value.nil?
}
],
output: SDK::Args::ConfigData,
func: method(:converter),
)
end
def converter(fv)
SDK::Args::ConfigData.decode(fv.value.value)
end
end
class ConfigMergeFromSpec < Mapper
include Util::HasLogger
def initialize
super(
inputs: [
Input.new(type: SDK::FuncSpec::Value) { |arg|
arg.type == "hashicorp.vagrant.sdk.Config.Merge" &&
!arg&.value&.value.nil?
}
],
output: SDK::Config::Merge,
func: method(:converter),
)
end
def converter(fv)
SDK::Config::Merge.decode(fv.value.value)
end
end
class ConfigFinalizeFromSpec < Mapper
def initialize
super(
inputs: [
Input.new(type: SDK::FuncSpec::Value) { |arg|
arg.type == "hashicorp.vagrant.sdk.Config.Finalize" &&
!arg&.value&.value.nil?
}
],
output: SDK::Config::Finalize,
func: method(:converter),
)
end
def converter(fv)
SDK::Config::Finalize.decode(fv.value.value)
end
end
class ConfigFinalizeResponseProtoFromConfigDataProto < Mapper
def initialize
super(
inputs: [
Input.new(type: SDK::Args::ConfigData),
],
output: SDK::Config::FinalizeResponse,
func: method(:converter),
)
end
def converter(c)
SDK::Config::FinalizeResponse.new(data: c)
end
end
class ConfigFromConfigDataProto < Mapper
include Util::HasLogger
def initialize
super(
inputs: [
Input.new(type: SDK::Args::ConfigData),
],
output: Vagrant::Plugin::V2::Config,
func: method(:converter)
)
end
def converter(c)
c.to_ruby
end
end
class RootConfigToHashProto < Mapper
include Util::HasLogger
def initialize
super(
inputs: [
Input.new(type: Vagrant::Config::V2::Root),
],
output: SDK::Args::Hash,
func: method(:converter),
)
end
def converter(c)
c.to_proto
end
end
class ConfigToProto < Mapper
include Util::HasLogger
def initialize
super(
inputs: [
Input.new(type: Vagrant::Plugin::V2::Config),
Input.new(type: Mappers),
],
output: SDK::Args::ConfigData,
func: method(:converter),
)
end
def converter(c, m)
data = Hash.new.tap do |h|
c.instance_variables.each do |v|
h[v.to_s.sub('@', '')] = c.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|
begin
SDK::Args::HashEntry.new(
key: m.map(k, to: Google::Protobuf::Any),
value: m.map(v, to: Google::Protobuf::Any),
)
rescue Internal::Graph::Search::NoPathError, TypeError
logger.warn("failed to map '#{k}' value of type `#{v.class}'")
nil
end
end.compact
SDK::Args::ConfigData.new(
data: SDK::Args::Hash.new(entries: entries),
source: m.map(c.class, to: SDK::Args::Class),
)
end
end
class RawRubyValueToProto < Mapper
include Util::HasLogger
def initialize
super(
inputs: [
Input.new(type: Object, origin_restricted: true),
Input.new(type: Mappers),
],
output: SDK::Config::RawRubyValue,
func: method(:converter)
)
end
def converter(o, m)
klass = m.map(o.class, to: SDK::Args::Class)
data = Hash.new.tap do |h|
o.instance_variables.each do |v|
h[v.to_s.sub('@', '')] = o.instance_variable_get(v)
end
end
entries = data.map do |k, v|
next if v.is_a?(Log4r::Logger)
begin
SDK::Args::HashEntry.new(
key: m.map(k, to: Google::Protobuf::Any),
value: m.map(v, to: Google::Protobuf::Any),
)
rescue Internal::Graph::Search::NoPathError, TypeError
logger.warn("failed to map '#{k}' value of type `#{v.class}'")
nil
end
end.compact
SDK::Config::RawRubyValue.new(
source: klass,
data: SDK::Args::Hash.new(entries: entries)
)
end
def extra_weight
1_000_000_000_000
end
end
class RawRubyValueFromProto < Mapper
include Util::HasLogger
def initialize
super(
inputs: [
Input.new(type: SDK::Config::RawRubyValue, origin_restricted: true),
Input.new(type: Mappers)
],
output: Object,
func: method(:converter),
)
end
def converter(r, m)
base_klass = m.map(r.source, to: Class)
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
data = m.map(r.data, to: Hash)
data.each_pair do |k, v|
instance.instance_variable_set("@#{k}", v)
end
instance
end
end
end
end
end