pipeline.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "go-common/app/admin/ep/saga/conf"
  8. "go-common/app/admin/ep/saga/model"
  9. "go-common/app/admin/ep/saga/service/utils"
  10. "go-common/app/admin/ep/saga/service/wechat"
  11. "go-common/library/cache/memcache"
  12. "go-common/library/log"
  13. "github.com/xanzy/go-gitlab"
  14. )
  15. const (
  16. _gitHTTP = "http://git.bilibili.co/"
  17. _gitSSH = "git@git-test.bilibili.co:"
  18. _gitSSHTail = ".git"
  19. _manualJob = "manual"
  20. _androidScheduleJob = "daily branch check"
  21. _iosScheduleJob = "daily:on-schedule"
  22. )
  23. // QueryTeamPipeline query pipeline info according to team.
  24. func (s *Service) QueryTeamPipeline(c context.Context, req *model.TeamDataRequest) (resp *model.PipelineDataResp, err error) {
  25. var (
  26. projectInfo []*model.ProjectInfo
  27. reqProject = &model.ProjectInfoRequest{}
  28. data []*model.PipelineDataTime
  29. queryDes string
  30. total int
  31. succNum int
  32. key string
  33. keyNotExist bool
  34. )
  35. if len(req.Department) <= 0 && len(req.Business) <= 0 {
  36. log.Warn("query department and business are empty!")
  37. return
  38. }
  39. //get pipeline info from mc
  40. key = "saga_admin_" + req.Department + "_" + req.Business + "_" + model.KeyTypeConst[3]
  41. if resp, err = s.dao.GetPipeline(c, key); err != nil {
  42. if err == memcache.ErrNotFound {
  43. keyNotExist = true
  44. } else {
  45. return
  46. }
  47. } else {
  48. return
  49. }
  50. log.Info("sync team pipeline start => type= %d, Department= %s, Business= %s", req.QueryType, req.Department, req.Business)
  51. //query team projects
  52. reqProject.Department = req.Department
  53. reqProject.Business = req.Business
  54. if _, projectInfo, err = s.dao.QueryProjectInfo(false, reqProject); err != nil {
  55. return
  56. }
  57. if len(projectInfo) <= 0 {
  58. log.Warn("Found no project!")
  59. return
  60. }
  61. if data, total, succNum, err = s.QueryTeamPipelineByTime(projectInfo, model.LastWeekPerDay); err != nil {
  62. return
  63. }
  64. successScale := succNum * 100 / total
  65. queryDes = req.Department + " " + req.Business + " " + "pipeline上一周每天数量"
  66. resp = &model.PipelineDataResp{
  67. Department: req.Department,
  68. Business: req.Business,
  69. QueryDes: queryDes,
  70. Total: total,
  71. SuccessNum: succNum,
  72. SuccessScale: successScale,
  73. Data: data,
  74. }
  75. //set pipeline info to mc
  76. if keyNotExist {
  77. if err = s.dao.SetPipeline(c, key, resp); err != nil {
  78. return
  79. }
  80. }
  81. log.Info("sync team pipeline end")
  82. return
  83. }
  84. // QueryTeamPipelineByTime ...
  85. func (s *Service) QueryTeamPipelineByTime(projectInfo []*model.ProjectInfo, queryType int) (resp []*model.PipelineDataTime, allNum, succNum int, err error) {
  86. var (
  87. layout = "2006-01-02"
  88. since time.Time
  89. until time.Time
  90. total int
  91. success int
  92. count int
  93. )
  94. if queryType == model.LastWeekPerDay {
  95. count = model.DayNumPerWeek
  96. } else {
  97. log.Warn("Query Type is not in range!")
  98. return
  99. }
  100. year, month, day := time.Now().Date()
  101. weekDay := (int)(time.Now().Weekday())
  102. today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
  103. for i := 0; i < count; i++ {
  104. since = today.AddDate(0, 0, -weekDay-i)
  105. until = today.AddDate(0, 0, -weekDay-i+1)
  106. totalAll := 0
  107. successAll := 0
  108. //log.Info("== start query from: %v, to: %v", since, until)
  109. for _, project := range projectInfo {
  110. if total, success, err = s.QueryProjectPipeline(project.ProjectID, "success", since, until); err != nil {
  111. return
  112. }
  113. totalAll = totalAll + total
  114. successAll = successAll + success
  115. }
  116. perData := &model.PipelineDataTime{
  117. TotalItem: totalAll,
  118. SuccessItem: successAll,
  119. StartTime: since.Format(layout),
  120. EndTime: until.Format(layout),
  121. }
  122. resp = append(resp, perData)
  123. allNum = allNum + totalAll
  124. succNum = succNum + successAll
  125. }
  126. return
  127. }
  128. // QueryProjectPipeline query pipeline info according to project id.
  129. func (s *Service) QueryProjectPipeline(projectID int, state string, since, until time.Time) (totalNum, stateNum int, err error) {
  130. var (
  131. pipelineList gitlab.PipelineList
  132. pipeline *gitlab.Pipeline
  133. resp *gitlab.Response
  134. startQuery bool
  135. )
  136. if _, resp, err = s.gitlab.ListProjectPipelines(1, projectID, ""); err != nil {
  137. return
  138. }
  139. page := 1
  140. for page <= resp.TotalPages {
  141. if !startQuery {
  142. if pipelineList, _, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  143. return
  144. }
  145. if page == 1 && len(pipelineList) <= 0 {
  146. return
  147. }
  148. if pipeline, _, err = s.gitlab.GetPipeline(projectID, pipelineList[0].ID); err != nil {
  149. return
  150. }
  151. if pipeline.CreatedAt.After(until) {
  152. page++
  153. continue
  154. } else {
  155. startQuery = true
  156. page--
  157. continue
  158. }
  159. }
  160. if pipelineList, _, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  161. return
  162. }
  163. for _, v := range pipelineList {
  164. if pipeline, _, err = s.gitlab.GetPipeline(projectID, v.ID); err != nil {
  165. return
  166. }
  167. createTime := pipeline.CreatedAt
  168. //year, month, day := createTime.Date()
  169. //log.Info("index: %d createTime: %d, month: %d, day: %d", k, year, month, day)
  170. if createTime.After(since) && createTime.Before(until) {
  171. totalNum = totalNum + 1
  172. if pipeline.Status == state {
  173. stateNum = stateNum + 1
  174. }
  175. }
  176. if createTime.Before(since) {
  177. return
  178. }
  179. }
  180. page++
  181. }
  182. return
  183. }
  184. // QueryProjectPipelineNew ...
  185. func (s *Service) QueryProjectPipelineNew(c context.Context, req *model.PipelineDataReq) (resp *model.PipelineDataAvgResp, err error) {
  186. var (
  187. data []*model.PipelineDataAvg
  188. queryDes string
  189. total int
  190. totalStatus int
  191. avgDurationTime float64
  192. avgPendingTime float64
  193. avgRunningTime float64
  194. )
  195. log.Info("QuerySingleProjectData Type: %d", req.Type)
  196. switch req.Type {
  197. case model.LastYearPerMonth:
  198. queryDes = model.LastYearPerMonthNote
  199. case model.LastMonthPerDay:
  200. queryDes = model.LastMonthPerDayNote
  201. case model.LastYearPerDay:
  202. queryDes = model.LastYearPerDayNote
  203. default:
  204. log.Warn("QueryProjectCommit Type is not in range")
  205. return
  206. }
  207. queryDes = req.ProjectName + " pipeline " + req.State + " " + queryDes
  208. if data, total, totalStatus, avgDurationTime, avgPendingTime, avgRunningTime, err = s.QueryProjectByTimeNew(c, req, req.Type); err != nil {
  209. return
  210. }
  211. resp = &model.PipelineDataAvgResp{
  212. ProjectName: req.ProjectName,
  213. QueryDes: queryDes,
  214. Status: req.State,
  215. Total: total,
  216. TotalStatus: totalStatus,
  217. AvgDurationTime: avgDurationTime,
  218. AvgPendingTime: avgPendingTime,
  219. AvgRunningTime: avgRunningTime,
  220. Data: data,
  221. }
  222. return
  223. }
  224. // QueryProjectByTimeNew ...
  225. func (s *Service) QueryProjectByTimeNew(c context.Context, req *model.PipelineDataReq, queryType int) (resp []*model.PipelineDataAvg, allNum, allStatusNum int, avgDurationTime, avgPendingTime, avgRunningTime float64, err error) {
  226. var (
  227. layout = "2006-01-02"
  228. since time.Time
  229. until time.Time
  230. count int
  231. pendingTimeListAll []float64
  232. runningTimeListAll []float64
  233. durationTimeListAll []float64
  234. pipelineTime *model.PipelineTime
  235. perData *model.PipelineDataAvg
  236. avgTotalTime float64
  237. totalNum int
  238. statusNum int
  239. )
  240. year, month, day := time.Now().Date()
  241. thisMonth := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
  242. if queryType == model.LastYearPerMonth {
  243. count = model.MonthNumPerYear
  244. } else if queryType == model.LastMonthPerDay {
  245. //_, _, count = thisMonth.AddDate(0, 0, -1).Date()
  246. count = model.DayNumPerMonth
  247. } else if queryType == model.LastYearPerDay {
  248. count = model.DayNumPerYear
  249. }
  250. for i := count; i >= 1; i-- {
  251. if queryType == model.LastYearPerMonth {
  252. since = thisMonth.AddDate(0, -i, 0)
  253. until = thisMonth.AddDate(0, -i+1, 0)
  254. } else if queryType == model.LastMonthPerDay {
  255. since = thisMonth.AddDate(0, 0, -i)
  256. until = thisMonth.AddDate(0, 0, -i+1)
  257. } else if queryType == model.LastYearPerDay {
  258. since = thisMonth.AddDate(0, 0, -i)
  259. until = thisMonth.AddDate(0, 0, -i+1)
  260. }
  261. /*if totalNum, statusNum, pipelineTime, err = s.QueryProjectPipelines(c, req, since, until); err != nil {
  262. log.Error("QueryProjectPipelines err:%+v", err)
  263. return
  264. }*/
  265. if totalNum, statusNum, pipelineTime, err = s.QueryPipelinesFromDB(c, req, since, until); err != nil {
  266. log.Error("QueryPipelinesFromDB err:%+v", err)
  267. return
  268. }
  269. avgTotalTime = utils.CalAverageTime(req.StatisticsType, pipelineTime.DurationList)
  270. avgPendingTime = utils.CalAverageTime(req.StatisticsType, pipelineTime.PendingList)
  271. avgRunningTime = utils.CalAverageTime(req.StatisticsType, pipelineTime.RunningList)
  272. perData = &model.PipelineDataAvg{
  273. TotalItem: totalNum,
  274. TotalStatusItem: statusNum,
  275. AvgDurationTime: avgTotalTime,
  276. AvgPendingTime: avgPendingTime,
  277. AvgRunningTime: avgRunningTime,
  278. MaxDurationTime: pipelineTime.DurationMax,
  279. MinDurationTime: pipelineTime.DurationMin,
  280. MaxPendingTime: pipelineTime.PendingMax,
  281. MinPendingTime: pipelineTime.PendingMin,
  282. MaxRunningTime: pipelineTime.RunningMax,
  283. MinRunningTime: pipelineTime.RunningMin,
  284. StartTime: since.Format(layout),
  285. EndTime: until.Format(layout),
  286. }
  287. resp = append(resp, perData)
  288. allNum = allNum + totalNum
  289. allStatusNum = allStatusNum + statusNum
  290. pendingTimeListAll = utils.CombineSlice(pendingTimeListAll, pipelineTime.PendingList)
  291. runningTimeListAll = utils.CombineSlice(runningTimeListAll, pipelineTime.RunningList)
  292. durationTimeListAll = utils.CombineSlice(durationTimeListAll, pipelineTime.DurationList)
  293. }
  294. avgDurationTime = utils.CalAverageTime(req.StatisticsType, durationTimeListAll)
  295. avgPendingTime = utils.CalAverageTime(req.StatisticsType, pendingTimeListAll)
  296. avgRunningTime = utils.CalAverageTime(req.StatisticsType, runningTimeListAll)
  297. log.Info("avgDurationTime: %v, avgPendingTime: %v, avgRunningTime: %v", avgDurationTime, avgPendingTime, avgRunningTime)
  298. return
  299. }
  300. // QueryProjectPipelines ...
  301. func (s *Service) QueryProjectPipelines(c context.Context, req *model.PipelineDataReq, since, until time.Time) (totalNum, statusNum int, pipelineTime *model.PipelineTime, err error) {
  302. var (
  303. pipelineList gitlab.PipelineList
  304. pipeline *gitlab.Pipeline
  305. resp *gitlab.Response
  306. startQuery bool
  307. meetTime bool
  308. projectID = req.ProjectID
  309. pendingTime float64
  310. runningTime float64
  311. totalTime float64
  312. )
  313. pipelineTime = &model.PipelineTime{}
  314. opt := &gitlab.ListProjectPipelinesOptions{}
  315. if _, resp, err = s.gitlab.ListProjectPipelines(1, projectID, ""); err != nil {
  316. log.Error("ListProjectPipelines err: %+v", err)
  317. return
  318. }
  319. page := 1
  320. for page <= resp.TotalPages {
  321. opt.ListOptions.Page = page
  322. if !startQuery && (!since.IsZero() || !until.IsZero()) {
  323. if pipelineList, _, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  324. log.Error("ListProjectPipelines err: %+v", err)
  325. return
  326. }
  327. if page == 1 && len(pipelineList) <= 0 {
  328. return
  329. }
  330. if pipeline, _, err = s.gitlab.GetPipeline(projectID, pipelineList[0].ID); err != nil {
  331. return
  332. }
  333. if pipeline.CreatedAt.After(until) {
  334. page++
  335. continue
  336. } else {
  337. startQuery = true
  338. page--
  339. continue
  340. }
  341. }
  342. // start query
  343. if pipelineList, _, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  344. return
  345. }
  346. meetTime = true
  347. for _, v := range pipelineList {
  348. if pipeline, _, err = s.gitlab.GetPipeline(projectID, v.ID); err != nil {
  349. return
  350. }
  351. createTime := pipeline.CreatedAt
  352. if !since.IsZero() || !until.IsZero() {
  353. meetTime = createTime.After(since) && createTime.Before(until)
  354. }
  355. //the pipeline is we need
  356. if meetTime {
  357. totalNum = totalNum + 1
  358. if req.Branch != "" && req.Branch != pipeline.Ref {
  359. continue
  360. } else if req.User != "" && req.User != pipeline.User.Name {
  361. continue
  362. } else if req.State != "" && req.State != pipeline.Status {
  363. continue
  364. }
  365. statusNum = statusNum + 1
  366. if pipeline.Status != "cancel" {
  367. if pipeline.StartedAt == nil {
  368. pendingTime = 0
  369. runningTime = pipeline.FinishedAt.Sub(*pipeline.CreatedAt).Seconds()
  370. } else {
  371. pendingTime = pipeline.StartedAt.Sub(*pipeline.CreatedAt).Seconds()
  372. runningTime = pipeline.FinishedAt.Sub(*pipeline.StartedAt).Seconds()
  373. }
  374. totalTime = pipeline.FinishedAt.Sub(*pipeline.CreatedAt).Seconds()
  375. pipelineTime.PendingMax, pipelineTime.PendingMin = utils.CalSizeTime(pendingTime, pipelineTime.PendingMax, pipelineTime.PendingMin)
  376. pipelineTime.RunningMax, pipelineTime.RunningMin = utils.CalSizeTime(runningTime, pipelineTime.RunningMax, pipelineTime.RunningMin)
  377. pipelineTime.DurationMax, pipelineTime.DurationMin = utils.CalSizeTime(totalTime, pipelineTime.DurationMax, pipelineTime.DurationMin)
  378. pipelineTime.PendingList = append(pipelineTime.PendingList, pendingTime)
  379. pipelineTime.RunningList = append(pipelineTime.RunningList, runningTime)
  380. pipelineTime.DurationList = append(pipelineTime.DurationList, totalTime)
  381. }
  382. }
  383. // time is over, so return
  384. if (!since.IsZero() || !until.IsZero()) && createTime.Before(since) {
  385. return
  386. }
  387. }
  388. page++
  389. }
  390. return
  391. }
  392. // QueryPipelinesFromDB ...
  393. func (s *Service) QueryPipelinesFromDB(c context.Context, req *model.PipelineDataReq, since, until time.Time) (totalNum, statusNum int, pipelineTime *model.PipelineTime, err error) {
  394. var (
  395. fmtLayout = `%d-%d-%d 00:00:00`
  396. pipelines []*model.StatisticsPipeline
  397. projectID = req.ProjectID
  398. pendingTime float64
  399. runningTime float64
  400. totalTime float64
  401. )
  402. pipelineTime = &model.PipelineTime{}
  403. sinceStr := fmt.Sprintf(fmtLayout, since.Year(), since.Month(), since.Day())
  404. untilStr := fmt.Sprintf(fmtLayout, until.Year(), until.Month(), until.Day())
  405. if totalNum, statusNum, pipelines, err = s.dao.QueryPipelinesByTime(projectID, req, sinceStr, untilStr); err != nil {
  406. return
  407. }
  408. for _, pipeline := range pipelines {
  409. if pipeline.Status == model.StatusCancel {
  410. continue
  411. }
  412. if pipeline.FinishedAt == nil {
  413. continue
  414. }
  415. if pipeline.StartedAt == nil {
  416. pendingTime = 0
  417. runningTime = pipeline.FinishedAt.Sub(*pipeline.CreatedAt).Seconds()
  418. } else {
  419. pendingTime = pipeline.StartedAt.Sub(*pipeline.CreatedAt).Seconds()
  420. runningTime = pipeline.FinishedAt.Sub(*pipeline.StartedAt).Seconds()
  421. }
  422. totalTime = pipeline.FinishedAt.Sub(*pipeline.CreatedAt).Seconds()
  423. pipelineTime.PendingMax, pipelineTime.PendingMin = utils.CalSizeTime(pendingTime, pipelineTime.PendingMax, pipelineTime.PendingMin)
  424. pipelineTime.RunningMax, pipelineTime.RunningMin = utils.CalSizeTime(runningTime, pipelineTime.RunningMax, pipelineTime.RunningMin)
  425. pipelineTime.DurationMax, pipelineTime.DurationMin = utils.CalSizeTime(totalTime, pipelineTime.DurationMax, pipelineTime.DurationMin)
  426. pipelineTime.PendingList = append(pipelineTime.PendingList, pendingTime)
  427. pipelineTime.RunningList = append(pipelineTime.RunningList, runningTime)
  428. pipelineTime.DurationList = append(pipelineTime.DurationList, totalTime)
  429. }
  430. return
  431. }
  432. //alertProjectPipelineProc cron func
  433. func (s *Service) alertProjectPipelineProc() {
  434. for _, alert := range conf.Conf.Property.Git.AlertPipeline {
  435. projectId := alert.ProjectID
  436. runningTimeout := alert.RunningTimeout
  437. runningRate := alert.RunningRate
  438. runningThreshold := alert.RunningThreshold
  439. pendingTimeout := alert.PendingTimeout
  440. pendingThreshold := alert.PendingThreshold
  441. go func() {
  442. var err error
  443. if err = s.PipelineAlert(context.TODO(), projectId, runningTimeout, runningThreshold, runningRate, gitlab.Running); err != nil {
  444. log.Error("PipelineAlert Running (%+v)", err)
  445. }
  446. if err = s.PipelineAlert(context.TODO(), projectId, pendingTimeout, pendingThreshold, 0, gitlab.Pending); err != nil {
  447. log.Error("PipelineAlert Pending (%+v)", err)
  448. }
  449. }()
  450. }
  451. }
  452. //PipelineAlert ...
  453. func (s *Service) PipelineAlert(c context.Context, projectID, timeout, threshold, rate int, status gitlab.BuildStateValue) (err error) {
  454. var (
  455. layout = "2006-01-02 15:04:05"
  456. pipeline *gitlab.Pipeline
  457. timeoutNum int
  458. message string
  459. pipelineurl string
  460. durationTime float64
  461. pipelineSUM int
  462. timeoutPipeline string
  463. pipelineList gitlab.PipelineList
  464. resp *gitlab.Response
  465. projectInfo *model.ProjectInfo
  466. userlist = conf.Conf.Property.Git.UserList
  467. w = wechat.New(s.dao)
  468. sendMessage = false
  469. )
  470. if projectInfo, err = s.dao.ProjectInfoByID(projectID); err != nil {
  471. return
  472. }
  473. repo := projectInfo.Repo
  474. if len(repo) > len(_gitSSH) {
  475. repo = repo[len(_gitSSH) : len(repo)-len(_gitSSHTail)]
  476. repo = _gitHTTP + repo
  477. }
  478. timeNow := time.Now().Format(layout)
  479. message = fmt.Sprintf("[SAGA]Pipeline 告警 %v\n项目:%s\n", timeNow, repo)
  480. for page := 1; ; page++ {
  481. if pipelineList, resp, err = s.git.ListProjectPipelines(page, projectID, status); err != nil {
  482. return
  483. }
  484. for _, item := range pipelineList {
  485. pipelineSUM += 1
  486. if pipeline, _, err = s.git.GetPipeline(projectID, item.ID); err != nil {
  487. return
  488. }
  489. if status == gitlab.Pending {
  490. durationTime = pipeline.UpdatedAt.Sub(*pipeline.CreatedAt).Minutes()
  491. } else if status == gitlab.Running {
  492. //此处时间计算换成job
  493. if durationTime, err = s.PipelineRunningTime(projectID, item.ID); err != nil {
  494. return
  495. }
  496. }
  497. if int(durationTime) >= timeout {
  498. timeoutNum += 1
  499. pipelineurl = fmt.Sprintf("%d. %s/pipelines/%d (%vmin)\n", timeoutNum, repo, pipeline.ID, int(durationTime))
  500. timeoutPipeline += pipelineurl
  501. }
  502. }
  503. if resp.NextPage == 0 {
  504. break
  505. }
  506. }
  507. if timeoutPipeline != "" {
  508. message += fmt.Sprintf(`列表(url|%s时间):%s%s`, status, "\n", timeoutPipeline)
  509. }
  510. if pipelineSUM > 0 {
  511. message += fmt.Sprintf(`状态:%s 总数为%d个`, status, pipelineSUM)
  512. }
  513. if status == gitlab.Pending {
  514. var alertMessage string
  515. message += fmt.Sprintf(`%s告警:`, "\n")
  516. if pipelineSUM >= threshold {
  517. alertMessage = fmt.Sprintf(`[ 数量(%d)>=警戒值(%d) ]`, pipelineSUM, threshold)
  518. sendMessage = true
  519. }
  520. message += alertMessage
  521. if timeoutNum >= 1 {
  522. if alertMessage != "" {
  523. message = message[:strings.LastIndex(message, " ]")] + fmt.Sprintf(`,%s时间>=警戒值(%d) ]`, status, timeout)
  524. } else {
  525. message += fmt.Sprintf(`[ %s时间>=警戒值(%d) ]`, status, timeout)
  526. }
  527. sendMessage = true
  528. }
  529. }
  530. if status == gitlab.Running && timeoutNum >= threshold {
  531. sendMessage = true
  532. message += fmt.Sprintf(`,时间>%dmin为%d个%s告警:[ 数量(%d)>=警戒值(%d) ]`, timeout, timeoutNum, "\n", timeoutNum, threshold)
  533. if timeoutNum*100/pipelineSUM >= rate {
  534. message = message[:strings.LastIndex(message, " ]")] + fmt.Sprintf(`,比例(%v%s)>=警戒值%d%s ]`, timeoutNum*100/pipelineSUM, "%", rate, "%")
  535. }
  536. }
  537. if sendMessage {
  538. return w.PushMsg(c, userlist, message)
  539. }
  540. return
  541. }
  542. //PipelineRunningTime ...
  543. func (s *Service) PipelineRunningTime(projectID, pipelineID int) (durationTime float64, err error) {
  544. var jobList []*gitlab.Job
  545. if jobList, _, err = s.git.ListPipelineJobs(nil, projectID, pipelineID); err != nil {
  546. return
  547. }
  548. for _, job := range jobList {
  549. if job.Status != _manualJob && job.Name != _androidScheduleJob && job.Name != _iosScheduleJob {
  550. if job.FinishedAt != nil && job.StartedAt != nil {
  551. durationTime += job.FinishedAt.Sub(*job.StartedAt).Minutes()
  552. } else if job.StartedAt != nil {
  553. durationTime += time.Since(*job.StartedAt).Minutes()
  554. }
  555. }
  556. }
  557. return
  558. }
  559. /*-------------------------------------- sync pipeline ----------------------------------------*/
  560. // SyncProjectPipelines ...
  561. func (s *Service) SyncProjectPipelines(projectID int) (result *model.SyncResult, err error) {
  562. var (
  563. //syncAllTime = conf.Conf.Property.SyncData.SyncAllTime
  564. syncAllTime = false
  565. since *time.Time
  566. until *time.Time
  567. projectInfo *model.ProjectInfo
  568. )
  569. if projectInfo, err = s.dao.ProjectInfoByID(projectID); err != nil {
  570. return
  571. }
  572. if !syncAllTime {
  573. since, until = utils.CalSyncTime()
  574. log.Info("sync project id(%d), name(%s) pipeline, time since: %v, until: %v", projectID, projectInfo.Name, since, until)
  575. if result, err = s.SyncProjectPipelinesByTime(projectID, projectInfo.Name, *since, *until); err != nil {
  576. return
  577. }
  578. } else {
  579. log.Info("sync project id(%d), name(%s) pipeline", projectID, projectInfo.Name)
  580. if result, err = s.SyncProjectAllPipelines(projectID, projectInfo.Name); err != nil {
  581. return
  582. }
  583. }
  584. return
  585. }
  586. // SyncProjectPipelinesByTime ...
  587. func (s *Service) SyncProjectPipelinesByTime(projectID int, projectName string, since, until time.Time) (result *model.SyncResult, err error) {
  588. var (
  589. pipelines gitlab.PipelineList
  590. pipeline *gitlab.Pipeline
  591. resp *gitlab.Response
  592. startQuery bool
  593. )
  594. result = &model.SyncResult{}
  595. if _, resp, err = s.gitlab.ListProjectPipelines(1, projectID, ""); err != nil {
  596. return
  597. }
  598. page := 1
  599. for page <= resp.TotalPages {
  600. result.TotalPage++
  601. if !startQuery {
  602. if pipelines, resp, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  603. return
  604. }
  605. if page == 1 && len(pipelines) <= 0 {
  606. return
  607. }
  608. if pipeline, _, err = s.gitlab.GetPipeline(projectID, pipelines[0].ID); err != nil {
  609. return
  610. }
  611. if pipeline.CreatedAt.After(until) {
  612. page++
  613. continue
  614. } else {
  615. startQuery = true
  616. page--
  617. continue
  618. }
  619. }
  620. // start query
  621. if pipelines, _, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  622. return
  623. }
  624. for _, v := range pipelines {
  625. if pipeline, _, err = s.gitlab.GetPipeline(projectID, v.ID); err != nil {
  626. return
  627. }
  628. createTime := pipeline.CreatedAt
  629. if createTime.After(since) && createTime.Before(until) {
  630. if err = s.structureDBPipeline(projectID, projectName, pipeline); err != nil {
  631. log.Error("pipeline Save Database err: projectID(%d), PipelineID(%d)", projectID, pipeline.ID)
  632. err = nil
  633. errData := &model.FailData{
  634. ChildID: pipeline.ID,
  635. }
  636. result.FailData = append(result.FailData, errData)
  637. continue
  638. }
  639. result.TotalNum++
  640. }
  641. if createTime.Before(since) {
  642. return
  643. }
  644. }
  645. page++
  646. }
  647. return
  648. }
  649. // SyncProjectAllPipelines ...
  650. func (s *Service) SyncProjectAllPipelines(projectID int, projectName string) (result *model.SyncResult, err error) {
  651. var (
  652. pipelines gitlab.PipelineList
  653. pipeline *gitlab.Pipeline
  654. resp *gitlab.Response
  655. )
  656. result = &model.SyncResult{}
  657. for page := 1; ; page++ {
  658. result.TotalPage++
  659. if pipelines, resp, err = s.gitlab.ListProjectPipelines(page, projectID, ""); err != nil {
  660. return
  661. }
  662. for _, v := range pipelines {
  663. if pipeline, _, err = s.gitlab.GetPipeline(projectID, v.ID); err != nil {
  664. return
  665. }
  666. if err = s.structureDBPipeline(projectID, projectName, pipeline); err != nil {
  667. log.Error("pipeline Save Database err: projectID(%d), PipelineID(%d)", projectID, pipeline.ID)
  668. err = nil
  669. errData := &model.FailData{
  670. ChildID: pipeline.ID,
  671. }
  672. result.FailData = append(result.FailData, errData)
  673. continue
  674. }
  675. result.TotalNum++
  676. }
  677. if resp.NextPage == 0 {
  678. break
  679. }
  680. }
  681. return
  682. }
  683. // structureDBPipeline ...
  684. func (s *Service) structureDBPipeline(projectID int, projectName string, pipeline *gitlab.Pipeline) (err error) {
  685. statisticPipeline := &model.StatisticsPipeline{
  686. PipelineID: pipeline.ID,
  687. ProjectID: projectID,
  688. ProjectName: projectName,
  689. Status: pipeline.Status,
  690. Ref: pipeline.Ref,
  691. Tag: pipeline.Tag,
  692. User: pipeline.User.Name,
  693. CreatedAt: pipeline.CreatedAt,
  694. UpdatedAt: pipeline.UpdatedAt,
  695. StartedAt: pipeline.StartedAt,
  696. FinishedAt: pipeline.FinishedAt,
  697. CommittedAt: pipeline.CommittedAt,
  698. Coverage: pipeline.Coverage,
  699. Duration: pipeline.Duration,
  700. DurationTime: 0,
  701. }
  702. return s.SaveDatabasePipeline(statisticPipeline)
  703. }
  704. // SaveDatabasePipeline ...
  705. func (s *Service) SaveDatabasePipeline(pipelineDB *model.StatisticsPipeline) (err error) {
  706. var total int
  707. if total, err = s.dao.HasPipeline(pipelineDB.ProjectID, pipelineDB.PipelineID); err != nil {
  708. log.Error("SaveDatabasePipeline HasPipeline(%+v)", err)
  709. return
  710. }
  711. // found only one, so update
  712. if total == 1 {
  713. if err = s.dao.UpdatePipeline(pipelineDB.ProjectID, pipelineDB.PipelineID, pipelineDB); err != nil {
  714. log.Error("SaveDatabasePipeline UpdatePipeline err(%+v)", err)
  715. return
  716. }
  717. return
  718. } else if total > 1 {
  719. // found repeated row, this situation will not exist under normal
  720. log.Warn("SaveDatabasePipeline pipeline has more rows(%d)", total)
  721. return
  722. }
  723. // insert row now
  724. if err = s.dao.CreatePipeline(pipelineDB); err != nil {
  725. log.Error("SaveDatabasePipeline CreatePipeline err(%+v)", err)
  726. return
  727. }
  728. return
  729. }