1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
package plugin
import (
"fmt"
"io"
"strings"
"sync"
"github.com/hashicorp/go-argmapper"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
sdk "github.com/hashicorp/vagrant-plugin-sdk"
"github.com/hashicorp/vagrant-plugin-sdk/component"
"github.com/hashicorp/vagrant-plugin-sdk/core"
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/cacher"
"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/cleanup"
"github.com/hashicorp/vagrant/builtin/configvagrant"
"github.com/hashicorp/vagrant/builtin/httpdownloader"
"github.com/hashicorp/vagrant/builtin/myplugin"
"github.com/hashicorp/vagrant/builtin/otherplugin"
)
// Setting this value to `true` will run builtin plugins
// in the existing process. This mode of plugin execution
// is not a "supported" mode of the go-plugin library and
// currently should only be used during testing in development
// for determining impact of a large number of builtin
// plugins
const IN_PROCESS_PLUGINS = false
var (
// Builtins is the map of all available builtin plugins and their
// options for launching them.
Builtins = map[string][]sdk.Option{
"configvagrant": configvagrant.CommandOptions,
"myplugin": myplugin.CommandOptions,
"otherplugin": otherplugin.CommandOptions,
"httpdownloader": httpdownloader.PluginOptions,
}
)
type Plugin struct {
Builtin bool // Flags if this plugin is a builtin plugin
Cache cacher.Cache // Cache for plugins to utilize in mappers
Client plugin.ClientProtocol // Client connection to plugin
Location string // Location of the plugin (generally path to binary)
Mappers []*argmapper.Func // Plugin specific mappers
Name string // Name of the plugin
Types []component.Type // Component types supported by this plugin
Options map[component.Type]interface{} // Options for supported components
cleaner cleanup.Cleanup // Cleanup tasks to perform on closing
logger hclog.Logger
m sync.Mutex
manager *Manager // Plugin manager this plugin belongs to
src *plugin.Client // Client for the plugin
}
// Interface for plugins with mapper support
type HasMappers interface {
AppendMappers(...*argmapper.Func)
}
// Interface for plugins which allow broker access
type HasGRPCBroker interface {
GRPCBroker() *plugin.GRPCBroker
}
// Interface for plugins that allow setting request metadata
type HasPluginMetadata interface {
SetRequestMetadata(k, v string)
}
// Interface for plugins that support having a parent
type HasParent interface {
GetParentComponent() interface{}
Parent() (string, error)
SetParentComponent(interface{})
}
// Returns the plugin manager instance this plugin is attached
func (p *Plugin) Manager() *Manager {
return p.manager
}
// Get a component from the plugin. This will load the component via
// the configured plugin manager so all expected caching and configuration
// will occur.
func (p *Plugin) Component(t component.Type) (interface{}, error) {
i, err := p.manager.Find(p.Name, t)
if err != nil {
return nil, err
}
return i.Component, nil
}
// Check if plugin implements specific component type
func (p *Plugin) HasType(
t component.Type,
) bool {
for _, pt := range p.Types {
if pt == t {
return true
}
}
return false
}
// Add a callback to execute when plugin is closed
func (p *Plugin) Closer(c func() error) {
p.cleaner.Do(c)
}
// Calls all registered close callbacks
func (p *Plugin) Close() (err error) {
p.m.Lock()
defer p.m.Unlock()
return p.cleaner.Close()
}
// Get specific component type from plugin. This is not exported
// as it should not be called directly. The plugin manager should
// be used for loading component instances so all callbacks are
// applied appropriately and caching will be respected
func (p *Plugin) instanceOf(
c component.Type,
cfns []PluginConfigurator,
) (i *Instance, err error) {
p.m.Lock()
defer p.m.Unlock()
p.logger.Trace("loading component from plugin",
"name", p.Name,
"type", c.String())
if !p.HasType(c) {
p.logger.Error("unsupported component type requested",
"name", p.Name,
"type", c.String(),
"valid", p.types())
return nil, fmt.Errorf("plugin does not support %s component type", c.String())
}
// Build the instance
raw, err := p.Client.Dispense(strings.ToLower(c.String()))
if err != nil {
p.logger.Error("failed to dispense component from plugin",
"name", p.Name,
"type", c.String())
return
}
// Extract the GRPC broker if possible
b, ok := raw.(HasGRPCBroker)
if !ok {
p.logger.Error("cannot extract grpc broker from plugin client",
"component", c.String(),
"name", p.Name)
return nil, fmt.Errorf("unable to extract broker from plugin client")
}
// Include any mappers provided by the plugin
if cm, ok := raw.(HasMappers); ok {
cm.AppendMappers(p.Mappers...)
}
// Set the plugin name if possible
if named, ok := raw.(core.Named); ok {
named.SetPluginName(p.Name)
if err != nil {
return nil, err
}
}
// Create our instance
i = &Instance{
Component: raw,
Close: func() error {
if cl, ok := raw.(io.Closer); ok {
return cl.Close()
}
return nil
},
Broker: b.GRPCBroker(),
Mappers: p.Mappers,
Name: p.Name,
Type: c,
Options: p.Options[c],
}
// Be sure the instance is close when the plugin is closed
p.Closer(func() error {
return i.Close()
})
// Apply configurators to the instance
for _, fn := range cfns {
if err = fn(i, p.logger); err != nil {
return
}
}
return
}
// Helper that returns supported types as strings
func (p *Plugin) types() []string {
result := []string{}
for _, t := range p.Types {
result = append(result, t.String())
}
return result
}
|