vaguerent/internal/core/factory.go

130 lines
2.7 KiB
Go

package core
import (
"context"
"sync"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/cleanup"
"github.com/hashicorp/vagrant-plugin-sdk/terminal"
"github.com/hashicorp/vagrant/internal/plugin"
"github.com/hashicorp/vagrant/internal/serverclient"
)
type Factory struct {
ctx context.Context
cleanup cleanup.Cleanup
client *serverclient.VagrantClient
logger hclog.Logger
m sync.Mutex
plugins *plugin.Manager
registered map[string]*Basis
ui terminal.UI
}
func NewFactory(
ctx context.Context,
client *serverclient.VagrantClient,
logger hclog.Logger,
plugins *plugin.Manager,
ui terminal.UI,
) *Factory {
return &Factory{
ctx: ctx,
cleanup: cleanup.New(),
client: client,
logger: logger,
plugins: plugins,
ui: ui,
registered: map[string]*Basis{},
}
}
func (f *Factory) Closer(fn cleanup.CleanupFn) {
f.cleanup.Do(fn)
}
func (f *Factory) Close() error {
return f.cleanup.Close()
}
func (f *Factory) New(name string, opts ...BasisOption) (*Basis, error) {
f.m.Lock()
defer f.m.Unlock()
// If we have a name, check if it's registered and return
// the existing basis if available
if name != "" {
if b, ok := f.registered[name]; ok {
return b, nil
}
}
// Create a child plugin manager for the basis
// which we will close when the basis is closed
pm := f.plugins.Sub(name)
// Update the options to include this factory and
// our settings when creating the new basis
opts = append(opts,
WithFactory(f),
FromBasis(
&Basis{
ctx: f.ctx,
client: f.client,
logger: f.logger,
plugins: pm,
ui: f.ui,
},
),
)
b, err := NewBasis(f.ctx, opts...)
if err != nil {
return nil, err
}
// Now there's a chance we already have this basis
// registered if the name was not provided for
// an initial lookup. If it is registered, close
// this new basis, discard, and return the
// registered one
if existingB, ok := f.registered[b.Name()]; ok {
f.logger.Info("there was an existing basis so closing the new one",
"name", name,
"basis-name", b.Name(),
)
b.Close()
return existingB, nil
}
f.registered[b.Name()] = b
// Remove the basis from the registered list when closed
b.Closer(func() error {
f.m.Lock()
defer f.m.Unlock()
delete(f.registered, b.Name())
return nil
})
// Close the child plugin manager
b.Closer(func() error {
return pm.Close()
})
// Ensure any registered basis is closed
// when the factory is closed
f.Closer(func() (err error) {
for _, b := range f.registered {
berr := b.Close()
if berr != nil {
err = multierror.Append(err, berr)
}
}
return
})
return b, nil
}