323 lines
9.1 KiB
Go
323 lines
9.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"go-common/app/admin/ep/melloi/model"
|
|
"go-common/app/admin/ep/melloi/service/proto"
|
|
"go-common/library/ecode"
|
|
"go-common/library/log"
|
|
)
|
|
|
|
const (
|
|
_upCase = 1
|
|
_downCase = 0
|
|
)
|
|
|
|
// capitalize 首字母大小写处理
|
|
func (s *Service) capitalize(str string, tp int) string {
|
|
b := []rune(str)
|
|
if tp == _upCase {
|
|
if b[0] >= 97 && b[1] <= 122 {
|
|
b[0] -= 32
|
|
}
|
|
} else {
|
|
if b[0] >= 64 && b[0] <= 90 {
|
|
b[0] += 32
|
|
}
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
// ProtoParsing analyze proto file
|
|
func (s *Service) ProtoParsing(protoPath, protoName string) (res map[string]interface{}, err error) {
|
|
var pkgRetMap []string
|
|
res = make(map[string]interface{})
|
|
reader, err := os.Open(path.Join(protoPath, protoName))
|
|
if err != nil {
|
|
log.Error("open proto file error(%v)", err)
|
|
return nil, err
|
|
}
|
|
defer reader.Close()
|
|
parser := proto.NewParser(reader)
|
|
|
|
var proto *proto.Proto
|
|
proto, err = parser.Parse()
|
|
if err != nil {
|
|
log.Error("parse proto file error(%v)", err)
|
|
return nil, err
|
|
}
|
|
|
|
res["fileName"] = protoName
|
|
proto.Filename = strings.TrimSuffix(protoName, ".proto")
|
|
res["protoClass"] = s.capitalize(proto.Filename, _upCase)
|
|
|
|
if len(proto.Imports) > 0 {
|
|
var retImportMap []string
|
|
for _, impt := range proto.Imports {
|
|
retImportMap = append(retImportMap, impt.Filename)
|
|
}
|
|
res["import"] = retImportMap
|
|
}
|
|
|
|
if len(proto.Package) > 0 {
|
|
for _, pkg := range proto.Package {
|
|
pkgRetMap = append(pkgRetMap, pkg.Name)
|
|
}
|
|
res["package"] = pkgRetMap
|
|
}
|
|
|
|
if len(proto.Options) > 0 {
|
|
var retOptionsList []string
|
|
for _, opt := range proto.Options {
|
|
retOptionsList = append(retOptionsList, opt.Name)
|
|
// java_package proto
|
|
if opt.Name == "java_package" && len(proto.Package) <= 0 {
|
|
pkgRetMap = append(pkgRetMap, opt.Constant.Source)
|
|
}
|
|
}
|
|
res["package"] = pkgRetMap
|
|
res["options"] = retOptionsList
|
|
}
|
|
|
|
if len(proto.Services) > 0 {
|
|
var retImportMap []map[string]interface{}
|
|
for _, srv := range proto.Services {
|
|
firstTmp := make(map[string]interface{})
|
|
firstTmp["name"] = srv.Name
|
|
|
|
var secondMapTmp []map[string]interface{}
|
|
for _, rpc := range srv.RPCElements {
|
|
secondTmp := make(map[string]interface{})
|
|
method := s.capitalize(rpc.Name, _downCase)
|
|
// transfer get_by_uid to getByUid
|
|
if strings.ContainsAny(method, "_") {
|
|
var nmethod string
|
|
methodStrs := strings.Split(method, "_")
|
|
for i, item := range methodStrs {
|
|
if i != 0 {
|
|
item = s.capitalize(item, _upCase)
|
|
}
|
|
nmethod += item
|
|
}
|
|
method = nmethod
|
|
}
|
|
secondTmp["method"] = method
|
|
secondTmp["requestType"] = rpc.RequestType
|
|
secondTmp["returnType"] = rpc.ReturnsType
|
|
|
|
secondMapTmp = append(secondMapTmp, secondTmp)
|
|
}
|
|
firstTmp["rpc"] = secondMapTmp
|
|
retImportMap = append(retImportMap, firstTmp)
|
|
}
|
|
res["service"] = retImportMap
|
|
}
|
|
return
|
|
}
|
|
|
|
// CreateProtoImportDir create import dir
|
|
func (s *Service) CreateProtoImportDir(pathModel *model.ProtoPathModel) (err error) {
|
|
cMakeDir := fmt.Sprintf("mkdir -p %s ", path.Join(pathModel.RootPath, pathModel.ExtraPath))
|
|
if err = exec.Command("/bin/bash", "-c", cMakeDir).Run(); err != nil {
|
|
log.Error("create proto import dir error(%v)", err)
|
|
|
|
}
|
|
return
|
|
}
|
|
|
|
//GRPCQuickStart grpc quickstart
|
|
func (s *Service) GRPCQuickStart(c context.Context, request *model.GRPCQuickStartRequest, runUser string, cookies string) (ret map[string]string, err error) {
|
|
var (
|
|
g *model.GRPC
|
|
reportID int
|
|
)
|
|
addScriptReq := request.GRPCAddScriptRequest
|
|
ret = make(map[string]string)
|
|
if g, err = s.GRPCAddScript(c, &addScriptReq); err != nil {
|
|
log.Error("Save grpc script failed, (%v)", err)
|
|
return
|
|
}
|
|
if reportID, err = s.GRPCRunByModel(c, g, runUser, cookies); err != nil {
|
|
log.Error("performance test execution failed, (%v)", err)
|
|
return
|
|
}
|
|
ret["report_id"] = strconv.Itoa(reportID)
|
|
return
|
|
}
|
|
|
|
// SaveGRPCQuickStart save gRPC script of quick start
|
|
func (s *Service) SaveGRPCQuickStart(c context.Context, request *model.GRPCQuickStartRequest) (err error) {
|
|
addScriptReq := request.GRPCAddScriptRequest
|
|
_, err = s.GRPCAddScript(c, &addScriptReq)
|
|
return err
|
|
}
|
|
|
|
//GRPCAddScript create grpc script
|
|
func (s *Service) GRPCAddScript(c context.Context, request *model.GRPCAddScriptRequest) (g *model.GRPC, err error) {
|
|
// 若是debug ,则执行固定次数
|
|
if request.IsDebug == 1 {
|
|
request.Loops = 4
|
|
request.ThreadsSum = 1
|
|
request.TaskName += "_perf_debug" // debug tag
|
|
}
|
|
if g, err = s.CreateJmx(c, request); err != nil {
|
|
log.Error("create jmeter file error (%v)", err)
|
|
return
|
|
}
|
|
g.IsDebug = request.IsDebug
|
|
return s.dao.CreateGRPC(g)
|
|
}
|
|
|
|
// CreateJmx create jmx
|
|
func (s *Service) CreateJmx(c context.Context, request *model.GRPCAddScriptRequest) (g *model.GRPC, err error) {
|
|
if g, err = s.createJmeterFile(request); err != nil {
|
|
log.Error("create jmeter file error (%v)", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
//GRPCRunByScriptID grpc execution by id
|
|
func (s *Service) GRPCRunByScriptID(c context.Context, request *model.GRPCExecuteScriptRequest, runUser string, cookie string) (reportId int, err error) {
|
|
var grpc *model.GRPC
|
|
if grpc, err = s.dao.QueryGRPCByID(request.ScriptID); err != nil {
|
|
return
|
|
}
|
|
return s.GRPCRunByModel(c, grpc, runUser, cookie)
|
|
}
|
|
|
|
//GRPCRunByModel execute grpc by model data
|
|
func (s *Service) GRPCRunByModel(c context.Context, grpc *model.GRPC, runUser string, cookie string) (reportId int, err error) {
|
|
var resp model.DoPtestResp
|
|
tim := strconv.FormatInt(time.Now().Unix(), 10)
|
|
testNameNick := grpc.TaskName + tim
|
|
log.Info("开始调用压测grpc job-------\n")
|
|
|
|
ptestParam := model.DoPtestParam{
|
|
UserName: runUser, // 用户名
|
|
LoadTime: grpc.LoadTime, //运行时间
|
|
TestNames: StringToSlice(grpc.TaskName), //接口名转数组
|
|
FileName: grpc.JmxPath, // jmx文件
|
|
ResJtl: grpc.JtlLog, // jtl时间戳
|
|
JmeterLog: grpc.JmxLog, // jmeterlog时间戳
|
|
Department: grpc.Department,
|
|
Project: grpc.Project,
|
|
IsDebug: false,
|
|
APP: grpc.APP,
|
|
ScriptID: grpc.ID,
|
|
Cookie: cookie, // 用不到
|
|
URL: grpc.ServiceName, // 用于微信通知
|
|
LabelIDs: nil,
|
|
Domain: grpc.HostName, // 微信通知Domain
|
|
FileSplit: false, // 文件切割
|
|
SplitNum: 0, // 切割数量
|
|
JarPath: grpc.JarPath,
|
|
Type: model.PROTOCOL_GRPC, //grpc
|
|
}
|
|
if grpc.IsDebug == 1 {
|
|
ptestParam.IsDebug = true
|
|
}
|
|
if resp, err = s.DoPtestByJmeter(c, ptestParam, StringToSlice(testNameNick)); err != nil {
|
|
log.Error("s.DoPtestByJmeter err :(%v)", err)
|
|
return
|
|
}
|
|
reportId = resp.ReportSuID
|
|
return
|
|
}
|
|
|
|
//QueryGrpc query grpc list
|
|
func (s *Service) QueryGrpc(c context.Context, sessionID string, qgr *model.QueryGRPCRequest) (res *model.QueryGRPCResponse, err error) {
|
|
// 获取服务树节点
|
|
var (
|
|
treeNodes []string
|
|
treeNodesd []string
|
|
)
|
|
if treeNodesd, err = s.QueryUserRoleNode(c, sessionID); err != nil {
|
|
log.Error("QueryUserRoleNode err (%v):", err)
|
|
return
|
|
}
|
|
treeNodes = append(treeNodesd, "")
|
|
if ExistsInSlice(qgr.Executor, s.c.Melloi.Executor) {
|
|
return s.dao.QueryGRPCByWhiteName(&qgr.GRPC, qgr.PageNum, qgr.PageSize)
|
|
}
|
|
return s.dao.QueryGRPC(&qgr.GRPC, qgr.PageNum, qgr.PageSize, treeNodes)
|
|
}
|
|
|
|
//QueryGrpcById query grpc by id
|
|
func (s *Service) QueryGrpcById(id int) (*model.GRPC, error) {
|
|
return s.dao.QueryGRPCByID(id)
|
|
}
|
|
|
|
// UpdateGrpc update grpc
|
|
func (s *Service) UpdateGrpc(grpc *model.GRPC) error {
|
|
return s.dao.UpdateGRPC(grpc)
|
|
}
|
|
|
|
// DeleteGrpc delete grpc by id
|
|
func (s *Service) DeleteGrpc(id int) error {
|
|
return s.dao.DeleteGRPC(id)
|
|
}
|
|
|
|
//createJmx create jmx file, jtl file, jmx_log file
|
|
func (s *Service) createJmeterFile(grpcReq *model.GRPCAddScriptRequest) (g *model.GRPC, err error) {
|
|
var (
|
|
buff *template.Template
|
|
file *os.File
|
|
)
|
|
|
|
if buff, err = template.ParseFiles(s.c.Jmeter.GRPCTemplatePath); err != nil {
|
|
log.Error("open grpc.jmx failed! (%v)", err)
|
|
return nil, ecode.MelloiJmeterGenerateErr
|
|
}
|
|
|
|
g = model.GRPCReqToGRPC(grpcReq)
|
|
// 脚本执行限制
|
|
if g.LoadTime > s.c.Jmeter.TestTimeLimit {
|
|
g.LoadTime = s.c.Jmeter.TestTimeLimit
|
|
}
|
|
if g.Loops <= 0 {
|
|
g.Loops = -1
|
|
}
|
|
|
|
if g.IsAsync {
|
|
g.AsyncInfo = unescaped(model.AsyncInfo)
|
|
}
|
|
|
|
// 生成jmx文件
|
|
if grpcReq.ScriptPath == "" {
|
|
log.Error("proto file is not uploaded.")
|
|
return nil, ecode.MelloiProtoFileNotUploaded
|
|
}
|
|
|
|
g.JmxPath = path.Join(grpcReq.ScriptPath, grpcReq.TaskName+".jmx")
|
|
g.JmxLog = path.Join(grpcReq.ScriptPath, grpcReq.TaskName+".log") // 定义好即可,运行时生成
|
|
g.JtlLog = path.Join(grpcReq.ScriptPath, grpcReq.TaskName+".jtl") // 定义好即可,运行时生成
|
|
|
|
if g.ParamFilePath != "" {
|
|
g.ParamEnable = "true"
|
|
}
|
|
|
|
if file, err = os.Create(g.JmxPath); err != nil {
|
|
log.Error("create jmx file error :(%v)", err)
|
|
return nil, ecode.MelloiJmeterGenerateErr
|
|
}
|
|
defer file.Close()
|
|
// 写入模板数据
|
|
buff = buff.Funcs(template.FuncMap{"unescaped": unescaped})
|
|
if err = buff.Execute(io.Writer(file), g); err != nil {
|
|
log.Error("write jmeter file failed, (%v)", err)
|
|
return nil, ecode.MelloiJmeterGenerateErr
|
|
}
|
|
return
|
|
}
|