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 }