diff --git a/plugin/common/go_plugin.go b/plugin/common/go_plugin.go new file mode 100644 index 00000000..58716964 --- /dev/null +++ b/plugin/common/go_plugin.go @@ -0,0 +1,70 @@ +package common + +import ( + "fmt" + "plugin" + "reflect" + "runtime" + + "github.com/rs/zerolog/log" +) + +// GoPlugin implements golang official plugin +type GoPlugin struct { + *plugin.Plugin + cachedFunctions map[string]reflect.Value // cache loaded functions to improve performance +} + +func (p *GoPlugin) Init(path string) error { + if runtime.GOOS == "windows" { + log.Warn().Msg("go plugin does not support windows") + return fmt.Errorf("go plugin does not support windows") + } + + var err error + p.Plugin, err = plugin.Open(path) + if err != nil { + log.Error().Err(err).Str("path", path).Msg("load go plugin failed") + return err + } + + p.cachedFunctions = make(map[string]reflect.Value) + log.Info().Str("path", path).Msg("load go plugin success") + return nil +} + +func (p *GoPlugin) Has(funcName string) bool { + fn, ok := p.cachedFunctions[funcName] + if ok { + return fn.IsValid() + } + + sym, err := p.Plugin.Lookup(funcName) + if err != nil { + p.cachedFunctions[funcName] = reflect.Value{} // mark as invalid + return false + } + fn = reflect.ValueOf(sym) + + // check function type + if fn.Kind() != reflect.Func { + p.cachedFunctions[funcName] = reflect.Value{} // mark as invalid + return false + } + + p.cachedFunctions[funcName] = fn + return true +} + +func (p *GoPlugin) Call(funcName string, args ...interface{}) (interface{}, error) { + if !p.Has(funcName) { + return nil, fmt.Errorf("function %s not found", funcName) + } + fn := p.cachedFunctions[funcName] + return CallFunc(fn, args...) +} + +func (p *GoPlugin) Quit() error { + // no need to quit for go plugin + return nil +} diff --git a/plugin/common/hashicorp_plugin.go b/plugin/common/hashicorp_plugin.go new file mode 100644 index 00000000..199d4af7 --- /dev/null +++ b/plugin/common/hashicorp_plugin.go @@ -0,0 +1,60 @@ +package common + +import ( + pluginHost "github.com/httprunner/hrp/plugin/host" + pluginShared "github.com/httprunner/hrp/plugin/shared" + "github.com/rs/zerolog/log" +) + +// HashicorpPlugin implements hashicorp/go-plugin +type HashicorpPlugin struct { + pluginShared.FuncCaller + cachedFunctions map[string]bool // cache loaded functions to improve performance +} + +func (p *HashicorpPlugin) Init(path string) error { + + f, err := pluginHost.Init(path) + if err != nil { + log.Error().Err(err).Str("path", path).Msg("load go hashicorp plugin failed") + return err + } + p.FuncCaller = f + + p.cachedFunctions = make(map[string]bool) + log.Info().Str("path", path).Msg("load hashicorp go plugin success") + return nil +} + +func (p *HashicorpPlugin) Has(funcName string) bool { + flag, ok := p.cachedFunctions[funcName] + if ok { + return flag + } + + funcNames, err := p.GetNames() + if err != nil { + return false + } + + for _, name := range funcNames { + if name == funcName { + p.cachedFunctions[funcName] = true // cache as exists + return true + } + } + + p.cachedFunctions[funcName] = false // cache as not exists + return false +} + +func (p *HashicorpPlugin) Call(funcName string, args ...interface{}) (interface{}, error) { + return p.FuncCaller.Call(funcName, args...) +} + +func (p *HashicorpPlugin) Quit() error { + // kill hashicorp plugin process + log.Info().Msg("quit hashicorp plugin process") + pluginHost.Quit() + return nil +} diff --git a/plugin/common/init.go b/plugin/common/init.go index e8f757bf..35aa0a1c 100644 --- a/plugin/common/init.go +++ b/plugin/common/init.go @@ -4,13 +4,7 @@ import ( "fmt" "os" "path/filepath" - "plugin" - "reflect" - "runtime" - "github.com/rs/zerolog/log" - - pluginHost "github.com/httprunner/hrp/plugin/host" pluginShared "github.com/httprunner/hrp/plugin/shared" ) @@ -29,119 +23,6 @@ type Plugin interface { Quit() error // quit plugin } -// GoPlugin implements golang official plugin -type GoPlugin struct { - *plugin.Plugin - cachedFunctions map[string]reflect.Value // cache loaded functions to improve performance -} - -func (p *GoPlugin) Init(path string) error { - if runtime.GOOS == "windows" { - log.Warn().Msg("go plugin does not support windows") - return fmt.Errorf("go plugin does not support windows") - } - - var err error - p.Plugin, err = plugin.Open(path) - if err != nil { - log.Error().Err(err).Str("path", path).Msg("load go plugin failed") - return err - } - - p.cachedFunctions = make(map[string]reflect.Value) - log.Info().Str("path", path).Msg("load go plugin success") - return nil -} - -func (p *GoPlugin) Has(funcName string) bool { - fn, ok := p.cachedFunctions[funcName] - if ok { - return fn.IsValid() - } - - sym, err := p.Plugin.Lookup(funcName) - if err != nil { - p.cachedFunctions[funcName] = reflect.Value{} // mark as invalid - return false - } - fn = reflect.ValueOf(sym) - - // check function type - if fn.Kind() != reflect.Func { - p.cachedFunctions[funcName] = reflect.Value{} // mark as invalid - return false - } - - p.cachedFunctions[funcName] = fn - return true -} - -func (p *GoPlugin) Call(funcName string, args ...interface{}) (interface{}, error) { - if !p.Has(funcName) { - return nil, fmt.Errorf("function %s not found", funcName) - } - fn := p.cachedFunctions[funcName] - return CallFunc(fn, args...) -} - -func (p *GoPlugin) Quit() error { - // no need to quit for go plugin - return nil -} - -// HashicorpPlugin implements hashicorp/go-plugin -type HashicorpPlugin struct { - pluginShared.FuncCaller - cachedFunctions map[string]bool // cache loaded functions to improve performance -} - -func (p *HashicorpPlugin) Init(path string) error { - - f, err := pluginHost.Init(path) - if err != nil { - log.Error().Err(err).Str("path", path).Msg("load go hashicorp plugin failed") - return err - } - p.FuncCaller = f - - p.cachedFunctions = make(map[string]bool) - log.Info().Str("path", path).Msg("load hashicorp go plugin success") - return nil -} - -func (p *HashicorpPlugin) Has(funcName string) bool { - flag, ok := p.cachedFunctions[funcName] - if ok { - return flag - } - - funcNames, err := p.GetNames() - if err != nil { - return false - } - - for _, name := range funcNames { - if name == funcName { - p.cachedFunctions[funcName] = true // cache as exists - return true - } - } - - p.cachedFunctions[funcName] = false // cache as not exists - return false -} - -func (p *HashicorpPlugin) Call(funcName string, args ...interface{}) (interface{}, error) { - return p.FuncCaller.Call(funcName, args...) -} - -func (p *HashicorpPlugin) Quit() error { - // kill hashicorp plugin process - log.Info().Msg("quit hashicorp plugin process") - pluginHost.Quit() - return nil -} - func Init(path string) (Plugin, error) { if path == "" { return nil, nil