Files
threshold/sdk/plugins/host/options.go
T

120 lines
3.6 KiB
Go

package external_host_plugins
import (
"errors"
"fmt"
"io/fs"
"os"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-hclog"
)
// getOpts iterates the inbound Options and returns a struct
func getOpts(opt ...Option) (*options, error) {
opts := getDefaultOptions()
for _, o := range opt {
if o == nil {
continue
}
if err := o(opts); err != nil {
return nil, fmt.Errorf("error running option function: %w", err)
}
}
return opts, nil
}
// Option - a type that wraps an interface for compile-time safety but can
// contain an option for this package or for wrappers implementing this
// interface.
type Option func(*options) error
type options struct {
withHostPluginsSources []pluginSourceInfo
withHostPluginExecutionDir string
withLogger hclog.Logger
}
func getDefaultOptions() *options {
return &options{}
}
// pluginSourceInfo contains information about plugin sources. Each entry in the
// final slice of withHostPluginsSources will either have the Fs/FsPrefix values
// populated or have the map populated. The former is for loading and executing
// a plugin via go-plugin from the filesystem; the latter is for executing it
// directly in-memory.
type pluginSourceInfo struct {
pluginMap map[string]func() (pb.HostPluginServiceClient, error)
pluginFs fs.FS
pluginFsPrefix string
}
// WithLogger allows passing a logger to the plugin library for debugging
func WithLogger(logger hclog.Logger) Option {
return func(o *options) error {
o.withLogger = logger
return nil
}
}
// WithHostPluginsFilesystem provides an fs.FS containing plugins that can be
// executed to provide Host functionality. This can be specified multiple times;
// all FSes will be scanned. If there are conflicts, the last one wins (this
// property is shared with WithHostPluginsMap). The prefix will be stripped from
// each entry when determining the plugin type.
func WithHostPluginsFilesystem(prefix string, plugins fs.FS) Option {
return func(o *options) error {
if plugins == nil {
return errors.New("nil host plugin filesystem passed into option")
}
o.withHostPluginsSources = append(o.withHostPluginsSources,
pluginSourceInfo{
pluginFs: plugins,
pluginFsPrefix: prefix,
},
)
return nil
}
}
// WithHostPluginsMap provides a map containing functions that can be called to
// provide implementations of the server. This can be specified multiple times;
// all FSes will be scanned. If there are conflicts, the last one wins (this
// property is shared with WithHostPluginsFilesystem).
func WithHostPluginsMap(plugins map[string]func() (pb.HostPluginServiceClient, error)) Option {
return func(o *options) error {
if len(plugins) == 0 {
return errors.New("no entries in host plugins map passed into option")
}
o.withHostPluginsSources = append(o.withHostPluginsSources,
pluginSourceInfo{
pluginMap: plugins,
},
)
return nil
}
}
// WithHostPluginExecutionDir allows setting a specific directory for
// writing out and executing plugins; if not set, os.TempDir will be used
// to create a suitable directory.
func WithHostPluginExecutionDir(dir string) Option {
return func(o *options) error {
if dir == "" {
// We always call this with the option, so if it's not actually set,
// don't error
return nil
}
fi, err := os.Stat(dir)
if err != nil {
return fmt.Errorf("error while performing stat to validate path %q is a directory: %w", dir, err)
}
if !fi.IsDir() {
return fmt.Errorf("given plugin execution path %q is not a directory", dir)
}
o.withHostPluginExecutionDir = dir
return nil
}
}