Files
httprunner/hrp/plugin.go
2022-05-28 02:06:33 +08:00

157 lines
3.9 KiB
Go

package hrp
import (
"fmt"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"github.com/httprunner/funplugin"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/internal/build"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
)
const (
goPluginFile = "debugtalk.so" // built from go plugin
hashicorpGoPluginFile = "debugtalk.bin" // built from hashicorp go plugin
hashicorpPyPluginFile = "debugtalk_gen.py" // used for hashicorp python plugin, automatically generated by HRP
debugtalkPyFile = "debugtalk.py" // write by user
projectInfoFile = "proj.json" // used for ensuring root project
)
func initPlugin(path string, logOn bool) (plugin funplugin.IPlugin, pluginDir string, err error) {
// plugin file not found
if path == "" {
return nil, "", nil
}
pluginPath, err := locatePlugin(path)
if err != nil {
return nil, "", nil
}
// TODO: move pluginDir to funplugin
pluginDir = filepath.Dir(pluginPath)
// compatible the format of debugtalk.py with v2/v3
ext := filepath.Ext(pluginPath)
if ext == ".py" {
// skip if only debugtalk_gen.py exists
if !strings.HasSuffix(pluginPath, "debugtalk_gen.py") {
genPyPluginPath := filepath.Join(pluginDir, "debugtalk_gen.py")
err = build.Run(pluginPath, genPyPluginPath)
if err != nil {
log.Error().Err(err).Msgf(fmt.Sprintf("failed to build %s", pluginPath))
return
}
pluginPath = genPyPluginPath
}
}
// found plugin file
plugin, err = funplugin.Init(pluginPath, funplugin.WithLogOn(logOn))
if err != nil {
log.Error().Err(err).Msgf("init plugin failed: %s", pluginPath)
return
}
// catch Interrupt and SIGTERM signals to ensure plugin quitted
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
plugin.Quit()
}()
// report event for initializing plugin
event := sdk.EventTracking{
Category: "InitPlugin",
Action: fmt.Sprintf("Init %s plugin", plugin.Type()),
Value: 0, // success
}
if err != nil {
event.Value = 1 // failed
}
go sdk.SendEvent(event)
return
}
func locatePlugin(path string) (pluginPath string, err error) {
// priority: hashicorp plugin (debugtalk.bin > debugtalk.py > debugtalk_gen.py) > go plugin (debugtalk.so)
pluginPath, err = locateFile(path, hashicorpGoPluginFile)
if err == nil {
return
}
pluginPath, err = locateFile(path, debugtalkPyFile)
if err == nil {
return
}
pluginPath, err = locateFile(path, hashicorpPyPluginFile)
if err == nil {
return
}
pluginPath, err = locateFile(path, goPluginFile)
if err == nil {
return
}
log.Warn().Err(err).Str("path", path).Msg("plugin file not found")
return "", fmt.Errorf("plugin file not found")
}
// locateFile searches destFile upward recursively until system root dir
func locateFile(startPath string, destFile string) (string, error) {
stat, err := os.Stat(startPath)
if os.IsNotExist(err) {
return "", err
}
var startDir string
if stat.IsDir() {
startDir = startPath
} else {
startDir = filepath.Dir(startPath)
}
startDir, _ = filepath.Abs(startDir)
// convention over configuration
pluginPath := filepath.Join(startDir, destFile)
if _, err := os.Stat(pluginPath); err == nil {
return pluginPath, nil
}
// system root dir
parentDir, _ := filepath.Abs(filepath.Dir(startDir))
if parentDir == startDir {
return "", fmt.Errorf("searched to system root dir, plugin file not found")
}
return locateFile(parentDir, destFile)
}
func GetProjectRootDirPath(path string) (rootDir string, err error) {
pluginPath, err := locatePlugin(path)
if err == nil {
rootDir = filepath.Dir(pluginPath)
return
}
// fix: no debugtalk file in project but having proj.json created by startpeoject
projPath, err := locateFile(path, projectInfoFile)
if err == nil {
rootDir = filepath.Dir(projPath)
return
}
// failed to locate project root dir
// maybe project plugin debugtalk.xx and proj.json are not exist
// use current dir instead
return os.Getwd()
}