Reorganize vagrantfile implementation, support provider overrides
This commit is contained in:
parent
f50f700e5c
commit
97c51a56cd
@ -12,11 +12,12 @@ func _() {
|
||||
_ = x[VAGRANTFILE_BASIS-1]
|
||||
_ = x[VAGRANTFILE_PROJECT-2]
|
||||
_ = x[VAGRANTFILE_TARGET-3]
|
||||
_ = x[VAGRANTFILE_PROVIDER-4]
|
||||
}
|
||||
|
||||
const _LoadLocation_name = "BoxBasisProjectTarget"
|
||||
const _LoadLocation_name = "BoxBasisProjectTargetProvider"
|
||||
|
||||
var _LoadLocation_index = [...]uint8{0, 3, 8, 15, 21}
|
||||
var _LoadLocation_index = [...]uint8{0, 3, 8, 15, 21, 29}
|
||||
|
||||
func (i LoadLocation) String() string {
|
||||
if i >= LoadLocation(len(_LoadLocation_index)-1) {
|
||||
|
||||
@ -30,6 +30,7 @@ type originScope interface {
|
||||
Boxes() (core.BoxCollection, error)
|
||||
Broker() *goplugin.GRPCBroker
|
||||
Cache() cacher.Cache
|
||||
Closer(func() error)
|
||||
LoadTarget(...TargetOption) (*Target, error)
|
||||
}
|
||||
|
||||
@ -39,19 +40,19 @@ type originScope interface {
|
||||
type LoadLocation uint8
|
||||
|
||||
const (
|
||||
VAGRANTFILE_BOX LoadLocation = iota // Box
|
||||
VAGRANTFILE_BASIS // Basis
|
||||
VAGRANTFILE_PROJECT // Project
|
||||
VAGRANTFILE_TARGET // Target
|
||||
VAGRANTFILE_BOX LoadLocation = iota // Box
|
||||
VAGRANTFILE_BASIS // Basis
|
||||
VAGRANTFILE_PROJECT // Project
|
||||
VAGRANTFILE_TARGET // Target
|
||||
VAGRANTFILE_PROVIDER // Provider
|
||||
)
|
||||
|
||||
// These are the locations which can be used for
|
||||
// populating the root value in the vagrantfile
|
||||
var ValidRootLocations = []LoadLocation{
|
||||
VAGRANTFILE_BOX,
|
||||
VAGRANTFILE_BASIS,
|
||||
VAGRANTFILE_PROJECT,
|
||||
VAGRANTFILE_TARGET,
|
||||
var ValidRootLocations = map[LoadLocation]struct{}{
|
||||
VAGRANTFILE_BASIS: {},
|
||||
VAGRANTFILE_PROJECT: {},
|
||||
VAGRANTFILE_TARGET: {},
|
||||
}
|
||||
|
||||
// Registration entry for a config component
|
||||
@ -136,6 +137,7 @@ type source struct {
|
||||
|
||||
// And here's our Vagrantfile!
|
||||
type Vagrantfile struct {
|
||||
cache cacher.Cache // Cached used for storing target configs
|
||||
cleanup cleanup.Cleanup // Cleanup tasks to run on close
|
||||
logger hclog.Logger // Logger
|
||||
mappers []*argmapper.Func // Mappers
|
||||
@ -145,7 +147,7 @@ type Vagrantfile struct {
|
||||
rubyClient *serverclient.RubyVagrantClient // Client for the Ruby runtime
|
||||
sources map[LoadLocation]*source // Vagrantfile sources
|
||||
|
||||
internal interface{}
|
||||
internal interface{} // Internal instance used for running maps
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
@ -154,43 +156,6 @@ func (v *Vagrantfile) String() string {
|
||||
v.origin, v.registrations, v.sources)
|
||||
}
|
||||
|
||||
// Create a clone of the current Vagrantfile
|
||||
func (v *Vagrantfile) clone(name string, origin originScope) *Vagrantfile {
|
||||
reg := make(registrations, len(v.registrations))
|
||||
for k, v := range v.registrations {
|
||||
reg[k] = v
|
||||
}
|
||||
srcs := make(map[LoadLocation]*source, len(v.sources))
|
||||
for k, v := range v.sources {
|
||||
srcs[k] = v
|
||||
}
|
||||
newV := &Vagrantfile{
|
||||
cleanup: cleanup.New(),
|
||||
logger: v.logger.Named(name),
|
||||
mappers: v.mappers,
|
||||
origin: origin,
|
||||
registrations: reg,
|
||||
rubyClient: v.rubyClient,
|
||||
sources: srcs,
|
||||
}
|
||||
if origin != nil {
|
||||
int := plugin.NewInternal(
|
||||
origin.Broker(),
|
||||
origin.Cache(),
|
||||
newV.cleanup,
|
||||
newV.logger,
|
||||
newV.mappers,
|
||||
)
|
||||
newV.internal = int
|
||||
} else {
|
||||
newV.internal = v.internal
|
||||
}
|
||||
|
||||
v.Closer(func() error { return newV.Close() })
|
||||
|
||||
return newV
|
||||
}
|
||||
|
||||
// Create a new Vagrantfile instance
|
||||
func NewVagrantfile(
|
||||
o originScope,
|
||||
@ -212,6 +177,7 @@ func NewVagrantfile(
|
||||
}
|
||||
copy(mappers[len(protomappers.All)-1:len(protomappers.All)+len(m)], m)
|
||||
v := &Vagrantfile{
|
||||
cache: cacher.New(),
|
||||
cleanup: cleanup.New(),
|
||||
logger: l.Named("vagrantfile"),
|
||||
mappers: mappers,
|
||||
@ -326,26 +292,32 @@ func (v *Vagrantfile) Init() (err error) {
|
||||
"sources", v.sources,
|
||||
)
|
||||
|
||||
// Collect all the viable locations for the initial load
|
||||
locations := []LoadLocation{}
|
||||
for _, i := range ValidRootLocations {
|
||||
// Collect all the viable locations for the initial load
|
||||
for i := VAGRANTFILE_BOX; i <= VAGRANTFILE_PROVIDER; i++ {
|
||||
if _, ok := v.sources[i]; ok {
|
||||
locations = append(locations, i)
|
||||
}
|
||||
}
|
||||
|
||||
// If our final location is finalized, just load that directly
|
||||
// If our final location is finalized, and is a valid root location,
|
||||
// then we use that finalized value and return. What this effectively
|
||||
// allows is reusing a serialized Vagrantfile during a single run. Since
|
||||
// the Vagrantfile will be parsed during the init job, when the command
|
||||
// job runs, we won't need to redo the work.
|
||||
var s *source
|
||||
finalIdx := len(locations) - 1
|
||||
if finalIdx >= 0 {
|
||||
final := locations[finalIdx]
|
||||
s = v.sources[final]
|
||||
if s.finalized != nil {
|
||||
v.logger.Info("setting vagrantfile root to finalized data and exiting",
|
||||
"data", hclog.Fmt("%#v", s.finalized),
|
||||
)
|
||||
v.root = s.finalized
|
||||
return
|
||||
if _, ok := ValidRootLocations[final]; ok {
|
||||
s = v.sources[final]
|
||||
if s.finalized != nil {
|
||||
v.logger.Info("setting vagrantfile root to finalized data and exiting",
|
||||
"data", hclog.Fmt("%#v", s.finalized),
|
||||
)
|
||||
v.root = s.finalized
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +340,6 @@ func (v *Vagrantfile) Init() (err error) {
|
||||
}
|
||||
|
||||
// Store the finalized configuration in the final source
|
||||
|
||||
if s != nil {
|
||||
if err = v.setFinalized(s, v.root); err != nil {
|
||||
return
|
||||
@ -380,205 +351,6 @@ func (v *Vagrantfile) Init() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Finalize all configuration held within the provided
|
||||
// config data. This assumes the given config data is
|
||||
// the top level, with each key being the namespace
|
||||
// for a given config plugin
|
||||
func (v *Vagrantfile) finalize(
|
||||
conf *component.ConfigData, // root configuration data
|
||||
) (result *component.ConfigData, err error) {
|
||||
result = &component.ConfigData{
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
var c core.Config
|
||||
var r *component.ConfigData
|
||||
for k, val := range conf.Data {
|
||||
v.logger.Trace("starting configuration finalization",
|
||||
"namespace", k,
|
||||
)
|
||||
if c, err = v.componentForKey(k); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, ok := val.(*component.ConfigData)
|
||||
if !ok {
|
||||
v.logger.Error("invalid config type",
|
||||
"key", k,
|
||||
"type", hclog.Fmt("%T", val),
|
||||
"value", hclog.Fmt("%#v", val),
|
||||
)
|
||||
return nil, fmt.Errorf("config for %s is invalid type %T", k, val)
|
||||
}
|
||||
v.logger.Trace("finalizing configuration data",
|
||||
"namespace", k,
|
||||
"data", data,
|
||||
)
|
||||
if r, err = c.Finalize(data); err != nil {
|
||||
return
|
||||
}
|
||||
v.logger.Trace("finalized configuration data",
|
||||
"namespace", k,
|
||||
)
|
||||
result.Data[k] = r
|
||||
v.logger.Trace("finalized data has been stored in result",
|
||||
"namespace", k,
|
||||
)
|
||||
}
|
||||
|
||||
v.logger.Trace("configuration data finalization is now complete")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Convert a proto hash into config data
|
||||
func (v *Vagrantfile) generateConfig(
|
||||
value *vagrant_plugin_sdk.Args_Hash,
|
||||
) (*component.ConfigData, error) {
|
||||
raw, err := dynamic.Map(
|
||||
&vagrant_plugin_sdk.Args_ConfigData{Data: value},
|
||||
(**component.ConfigData)(nil),
|
||||
argmapper.ConverterFunc(v.mappers...),
|
||||
argmapper.Typed(
|
||||
context.Background(),
|
||||
v.logger,
|
||||
plugin.Internal(v.logger, v.mappers),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return raw.(*component.ConfigData), nil
|
||||
}
|
||||
|
||||
// Generate a config data instance by merging all unfinalized
|
||||
// data from each source that is requested. The result will
|
||||
// be the unfinalized result of all merged configuration.
|
||||
func (v *Vagrantfile) generate(
|
||||
locs ...LoadLocation, // order of sources to load
|
||||
) (c *component.ConfigData, err error) {
|
||||
if len(locs) == 0 {
|
||||
return &component.ConfigData{Data: map[string]interface{}{}}, nil
|
||||
}
|
||||
|
||||
c = v.sources[locs[0]].unfinalized
|
||||
|
||||
for idx := 1; idx < len(locs); idx++ {
|
||||
i := locs[idx]
|
||||
v.logger.Trace("starting vagrantfile merge",
|
||||
"location", i.String(),
|
||||
)
|
||||
s, ok := v.sources[i]
|
||||
if !ok {
|
||||
v.logger.Warn("no vagrantfile set for location",
|
||||
"location", i.String(),
|
||||
)
|
||||
continue
|
||||
}
|
||||
if c == nil {
|
||||
v.logger.Trace("config unset, using location as base",
|
||||
"location", i.String(),
|
||||
)
|
||||
c = s.unfinalized
|
||||
continue
|
||||
}
|
||||
if c, err = v.merge(c, s.unfinalized); err != nil {
|
||||
v.logger.Trace("failed to merge vagrantfile",
|
||||
"location", i.String(),
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
v.logger.Trace("completed vagrantfile merge",
|
||||
"location", i.String(),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get the configuration component for the given namespace
|
||||
func (v *Vagrantfile) componentForKey(
|
||||
namespace string, // namespace config component is registered under
|
||||
) (core.Config, error) {
|
||||
reg := v.registrations[namespace]
|
||||
if reg == nil || reg.plugin == nil {
|
||||
return nil, fmt.Errorf("no plugin set for config namespace '%s'", namespace)
|
||||
}
|
||||
c, err := reg.plugin.Component(component.ConfigType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.(core.Config), nil
|
||||
}
|
||||
|
||||
// Merge two config data instances
|
||||
func (v *Vagrantfile) merge(
|
||||
base, // initial config data
|
||||
toMerge *component.ConfigData, // config data to merge into base
|
||||
) (*component.ConfigData, error) {
|
||||
result := &component.ConfigData{
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Collect all the keys we have available
|
||||
keys := map[string]struct{}{}
|
||||
for k, _ := range base.Data {
|
||||
keys[k] = struct{}{}
|
||||
}
|
||||
for k, _ := range toMerge.Data {
|
||||
keys[k] = struct{}{}
|
||||
}
|
||||
|
||||
for k, _ := range keys {
|
||||
c, err := v.componentForKey(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawBase, ok1 := base.Data[k]
|
||||
rawToMerge, ok2 := toMerge.Data[k]
|
||||
|
||||
if ok1 && !ok2 {
|
||||
result.Data[k] = rawBase
|
||||
v.logger.Debug("only base value available, no merge performed",
|
||||
"namespace", k,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if !ok1 && ok2 {
|
||||
result.Data[k] = rawToMerge
|
||||
v.logger.Debug("only toMerge value available, no merge performed",
|
||||
"namespace", k,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
var ok bool
|
||||
var valBase, valToMerge *component.ConfigData
|
||||
valBase, ok = rawBase.(*component.ConfigData)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bad value type for merge %T", rawBase)
|
||||
}
|
||||
valToMerge, ok = rawToMerge.(*component.ConfigData)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bad value type for merge %T", rawToMerge)
|
||||
}
|
||||
|
||||
v.logger.Debug("merging values",
|
||||
"namespace", k,
|
||||
)
|
||||
|
||||
r, err := c.Merge(valBase, valToMerge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Data[k] = r
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Get the configuration for the given namespace
|
||||
func (v *Vagrantfile) GetConfig(
|
||||
namespace string, // top level key in vagrantfile
|
||||
@ -680,11 +452,29 @@ func (v *Vagrantfile) Target(
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to actual Vagrantfile for target setup
|
||||
vf := conf.(*Vagrantfile)
|
||||
|
||||
target, err = v.origin.LoadTarget(
|
||||
WithTargetName(name),
|
||||
WithTargetVagrantfile(conf.(*Vagrantfile)),
|
||||
WithTargetVagrantfile(vf),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Since the target config gives us a Vagrantfile which is
|
||||
// attached to the project, we need to clone it and attach
|
||||
// it to the target we loaded
|
||||
rawTarget := target.(*Target)
|
||||
tvf := v.clone(name, rawTarget)
|
||||
rawTarget.vagrantfile = tvf
|
||||
|
||||
if err = vf.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -698,6 +488,11 @@ func (v *Vagrantfile) TargetConfig(
|
||||
v.m.Lock()
|
||||
defer v.m.Unlock()
|
||||
|
||||
cid := name + "+" + provider
|
||||
if cv := v.cache.Get(cid); cv != nil {
|
||||
return cv.(core.Vagrantfile), nil
|
||||
}
|
||||
|
||||
subvm, err := v.GetValue("vm", "__defined_vms", name)
|
||||
if err != nil {
|
||||
v.logger.Error("failed to get subvm",
|
||||
@ -728,6 +523,11 @@ func (v *Vagrantfile) TargetConfig(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.logger.Info("subvm configuration generated for target",
|
||||
"target", name,
|
||||
"config", resp,
|
||||
)
|
||||
|
||||
newV := v.clone(name, v.origin)
|
||||
err = newV.Source(
|
||||
&vagrant_server.Vagrantfile{
|
||||
@ -740,10 +540,30 @@ func (v *Vagrantfile) TargetConfig(
|
||||
return nil, fmt.Errorf("failed to add target config source: %w", err)
|
||||
}
|
||||
|
||||
if provider != "" {
|
||||
resp, err = v.rubyClient.ParseVagrantfileProvider(provider,
|
||||
subvm.(*vagrant_plugin_sdk.Config_RawRubyValue),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = newV.Source(
|
||||
&vagrant_server.Vagrantfile{
|
||||
Unfinalized: resp,
|
||||
},
|
||||
VAGRANTFILE_PROVIDER,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to add provider config source: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = newV.Init(); err != nil {
|
||||
return nil, fmt.Errorf("failed to init target config vagrantfile: %w", err)
|
||||
}
|
||||
|
||||
v.cache.Register(cid, newV)
|
||||
|
||||
return newV, nil
|
||||
}
|
||||
|
||||
@ -867,33 +687,6 @@ func (v *Vagrantfile) getNamespace(
|
||||
return c.Data
|
||||
}
|
||||
|
||||
// Set the finalized value for the given source. This
|
||||
// will convert the finalized data to proto and update
|
||||
// the backing Vagrantfile proto.
|
||||
func (v *Vagrantfile) setFinalized(
|
||||
s *source, // source to update
|
||||
f *component.ConfigData, // finalized data
|
||||
) error {
|
||||
s.finalized = f
|
||||
|
||||
raw, err := dynamic.Map(
|
||||
s.finalized.Data,
|
||||
(**vagrant_plugin_sdk.Args_Hash)(nil),
|
||||
argmapper.ConverterFunc(v.mappers...),
|
||||
argmapper.Typed(
|
||||
context.Background(),
|
||||
v.logger,
|
||||
plugin.Internal(v.logger, v.mappers),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.base.Finalized = raw.(*vagrant_plugin_sdk.Args_Hash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Converts the current root value into proto for storing in the origin
|
||||
func (v *Vagrantfile) rootToStore() (*vagrant_plugin_sdk.Args_ConfigData, error) {
|
||||
raw, err := dynamic.Map(
|
||||
@ -936,3 +729,269 @@ func (v *Vagrantfile) newSource(
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Finalize all configuration held within the provided
|
||||
// config data. This assumes the given config data is
|
||||
// the top level, with each key being the namespace
|
||||
// for a given config plugin
|
||||
func (v *Vagrantfile) finalize(
|
||||
conf *component.ConfigData, // root configuration data
|
||||
) (result *component.ConfigData, err error) {
|
||||
result = &component.ConfigData{
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
var c core.Config
|
||||
var r *component.ConfigData
|
||||
for k, val := range conf.Data {
|
||||
v.logger.Trace("starting configuration finalization",
|
||||
"namespace", k,
|
||||
)
|
||||
if c, err = v.componentForKey(k); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, ok := val.(*component.ConfigData)
|
||||
if !ok {
|
||||
v.logger.Error("invalid config type",
|
||||
"key", k,
|
||||
"type", hclog.Fmt("%T", val),
|
||||
"value", hclog.Fmt("%#v", val),
|
||||
)
|
||||
return nil, fmt.Errorf("config for %s is invalid type %T", k, val)
|
||||
}
|
||||
v.logger.Trace("finalizing configuration data",
|
||||
"namespace", k,
|
||||
"data", data,
|
||||
)
|
||||
if r, err = c.Finalize(data); err != nil {
|
||||
return
|
||||
}
|
||||
v.logger.Trace("finalized configuration data",
|
||||
"namespace", k,
|
||||
)
|
||||
result.Data[k] = r
|
||||
v.logger.Trace("finalized data has been stored in result",
|
||||
"namespace", k,
|
||||
)
|
||||
}
|
||||
|
||||
v.logger.Trace("configuration data finalization is now complete")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Set the finalized value for the given source. This
|
||||
// will convert the finalized data to proto and update
|
||||
// the backing Vagrantfile proto.
|
||||
func (v *Vagrantfile) setFinalized(
|
||||
s *source, // source to update
|
||||
f *component.ConfigData, // finalized data
|
||||
) error {
|
||||
s.finalized = f
|
||||
|
||||
raw, err := dynamic.Map(
|
||||
s.finalized.Data,
|
||||
(**vagrant_plugin_sdk.Args_Hash)(nil),
|
||||
argmapper.ConverterFunc(v.mappers...),
|
||||
argmapper.Typed(
|
||||
context.Background(),
|
||||
v.logger,
|
||||
plugin.Internal(v.logger, v.mappers),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.base.Finalized = raw.(*vagrant_plugin_sdk.Args_Hash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate a config data instance by merging all unfinalized
|
||||
// data from each source that is requested. The result will
|
||||
// be the unfinalized result of all merged configuration.
|
||||
func (v *Vagrantfile) generate(
|
||||
locs ...LoadLocation, // order of sources to load
|
||||
) (c *component.ConfigData, err error) {
|
||||
if len(locs) == 0 {
|
||||
return &component.ConfigData{Data: map[string]interface{}{}}, nil
|
||||
}
|
||||
|
||||
c = v.sources[locs[0]].unfinalized
|
||||
|
||||
for idx := 1; idx < len(locs); idx++ {
|
||||
i := locs[idx]
|
||||
v.logger.Trace("starting vagrantfile merge",
|
||||
"location", i.String(),
|
||||
)
|
||||
s, ok := v.sources[i]
|
||||
if !ok {
|
||||
v.logger.Warn("no vagrantfile set for location",
|
||||
"location", i.String(),
|
||||
)
|
||||
continue
|
||||
}
|
||||
if c == nil {
|
||||
v.logger.Trace("config unset, using location as base",
|
||||
"location", i.String(),
|
||||
)
|
||||
c = s.unfinalized
|
||||
continue
|
||||
}
|
||||
if c, err = v.merge(c, s.unfinalized); err != nil {
|
||||
v.logger.Trace("failed to merge vagrantfile",
|
||||
"location", i.String(),
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
v.logger.Trace("completed vagrantfile merge",
|
||||
"location", i.String(),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Convert a proto hash into config data
|
||||
func (v *Vagrantfile) generateConfig(
|
||||
value *vagrant_plugin_sdk.Args_Hash,
|
||||
) (*component.ConfigData, error) {
|
||||
raw, err := dynamic.Map(
|
||||
&vagrant_plugin_sdk.Args_ConfigData{Data: value},
|
||||
(**component.ConfigData)(nil),
|
||||
argmapper.ConverterFunc(v.mappers...),
|
||||
argmapper.Typed(
|
||||
context.Background(),
|
||||
v.logger,
|
||||
plugin.Internal(v.logger, v.mappers),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return raw.(*component.ConfigData), nil
|
||||
}
|
||||
|
||||
// Get the configuration component for the given namespace
|
||||
func (v *Vagrantfile) componentForKey(
|
||||
namespace string, // namespace config component is registered under
|
||||
) (core.Config, error) {
|
||||
reg := v.registrations[namespace]
|
||||
if reg == nil || reg.plugin == nil {
|
||||
return nil, fmt.Errorf("no plugin set for config namespace '%s'", namespace)
|
||||
}
|
||||
c, err := reg.plugin.Component(component.ConfigType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.(core.Config), nil
|
||||
}
|
||||
|
||||
// Merge two config data instances
|
||||
func (v *Vagrantfile) merge(
|
||||
base, // initial config data
|
||||
toMerge *component.ConfigData, // config data to merge into base
|
||||
) (*component.ConfigData, error) {
|
||||
result := &component.ConfigData{
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Collect all the keys we have available
|
||||
keys := map[string]struct{}{}
|
||||
for k, _ := range base.Data {
|
||||
keys[k] = struct{}{}
|
||||
}
|
||||
for k, _ := range toMerge.Data {
|
||||
keys[k] = struct{}{}
|
||||
}
|
||||
|
||||
for k, _ := range keys {
|
||||
c, err := v.componentForKey(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawBase, ok1 := base.Data[k]
|
||||
rawToMerge, ok2 := toMerge.Data[k]
|
||||
|
||||
if ok1 && !ok2 {
|
||||
result.Data[k] = rawBase
|
||||
v.logger.Debug("only base value available, no merge performed",
|
||||
"namespace", k,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if !ok1 && ok2 {
|
||||
result.Data[k] = rawToMerge
|
||||
v.logger.Debug("only toMerge value available, no merge performed",
|
||||
"namespace", k,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
var ok bool
|
||||
var valBase, valToMerge *component.ConfigData
|
||||
valBase, ok = rawBase.(*component.ConfigData)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bad value type for merge %T", rawBase)
|
||||
}
|
||||
valToMerge, ok = rawToMerge.(*component.ConfigData)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("bad value type for merge %T", rawToMerge)
|
||||
}
|
||||
|
||||
v.logger.Debug("merging values",
|
||||
"namespace", k,
|
||||
)
|
||||
|
||||
r, err := c.Merge(valBase, valToMerge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Data[k] = r
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Create a clone of the current Vagrantfile
|
||||
func (v *Vagrantfile) clone(name string, origin originScope) *Vagrantfile {
|
||||
reg := make(registrations, len(v.registrations))
|
||||
for k, v := range v.registrations {
|
||||
reg[k] = v
|
||||
}
|
||||
srcs := make(map[LoadLocation]*source, len(v.sources))
|
||||
for k, v := range v.sources {
|
||||
srcs[k] = v
|
||||
}
|
||||
newV := &Vagrantfile{
|
||||
cache: cacher.New(),
|
||||
cleanup: cleanup.New(),
|
||||
logger: v.logger.Named(name),
|
||||
mappers: v.mappers,
|
||||
origin: origin,
|
||||
registrations: reg,
|
||||
rubyClient: v.rubyClient,
|
||||
sources: srcs,
|
||||
}
|
||||
if origin != nil {
|
||||
int := plugin.NewInternal(
|
||||
origin.Broker(),
|
||||
origin.Cache(),
|
||||
newV.cleanup,
|
||||
newV.logger,
|
||||
newV.mappers,
|
||||
)
|
||||
newV.internal = int
|
||||
} else {
|
||||
newV.internal = v.internal
|
||||
}
|
||||
|
||||
origin.Closer(func() error { return newV.Close() })
|
||||
|
||||
return newV
|
||||
}
|
||||
|
||||
var _ core.Vagrantfile = (*Vagrantfile)(nil)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user