You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
9.1 KiB
322 lines
9.1 KiB
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 |
|
}
|
|
|