deploy.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. package service
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "gadmin/config"
  8. "gadmin/internal/admin/consts"
  9. "gadmin/internal/admin/forms"
  10. "gadmin/internal/admin/library/docker"
  11. "gadmin/internal/admin/ws"
  12. "gadmin/internal/gorm/model"
  13. "gadmin/internal/gorm/query"
  14. "gadmin/utility"
  15. "gadmin/utility/character"
  16. "gadmin/utility/serializer"
  17. "gadmin/utility/token"
  18. "os"
  19. "strconv"
  20. "strings"
  21. "time"
  22. "github.com/docker/docker/api/types"
  23. "github.com/gin-gonic/gin"
  24. "github.com/sirupsen/logrus"
  25. "gorm.io/gorm"
  26. )
  27. var Deploy = new(sDeploy)
  28. type sDeploy struct{}
  29. // 一个容器部署完成后的消息上报
  30. func (s *sDeploy) Notify(ctx *gin.Context, req forms.DeployNotifyReq) serializer.Response {
  31. var (
  32. q = query.Use(config.DB).ServerDeploy
  33. ql = query.Use(config.DB).ServerDeployLog
  34. qs = query.Use(config.DB).ServerDeployStat
  35. stat *model.ServerDeployStat
  36. err error
  37. status = consts.DeployStatusSuc
  38. )
  39. // 本次部署针对的容器 及相关配置
  40. taskItem, err := ql.Where(ql.TraceID.Eq(req.TraceID)).First()
  41. if err != nil {
  42. logrus.Warnf("first err:%+v", err)
  43. return serializer.DBErr(err.Error(), err)
  44. }
  45. if taskItem == nil {
  46. return serializer.DBErr("操作记录不存在", nil)
  47. }
  48. // 取出容器配置
  49. deploy, err := q.Where(q.ID.Eq(taskItem.DeployID)).First()
  50. if err != nil {
  51. logrus.Warnf("deploy err:%+v", err)
  52. return serializer.DBErr(err.Error(), err)
  53. }
  54. if deploy == nil {
  55. return serializer.DBErr("服务器不存在", nil)
  56. }
  57. // 取本次部署统计信息 一次部署可能是多个容器
  58. if taskItem.PublishType == consts.DeployPublishTypeVersion {
  59. stat, err = qs.Where(qs.BatchID.Eq(taskItem.BatchID)).First()
  60. if err != nil {
  61. logrus.Warnf("stat err:%+v", err)
  62. return serializer.DBErr(err.Error(), err)
  63. }
  64. if stat == nil {
  65. return serializer.DBErr("批量记录不存在", nil)
  66. }
  67. // 解析一个容器上报数据
  68. parsingError := func(log *forms.DeployLogViewModel) {
  69. if log == nil {
  70. return
  71. }
  72. logrus.Warnf("parsingError log:%+v\n", log)
  73. if strings.Contains(log.LogContent, `level=error`) {
  74. req.Code = 1
  75. req.Msg = fmt.Sprintf("拉取json配置异常,请检查")
  76. return
  77. }
  78. // 提取本次启动的进程
  79. ProcessFlag := "要开始的进程内容:"
  80. line, err1 := extractFromFlagToEnd(log.LogContent, ProcessFlag)
  81. if err1 != nil {
  82. req.Code = 1
  83. req.Msg = fmt.Sprintf("未找到本次需要启动的进程信息,请检查")
  84. return
  85. }
  86. logrus.Infof("本次启动进程: %v\n", line)
  87. extra := make(map[string]interface{})
  88. err := json.Unmarshal([]byte(taskItem.Extra), &extra)
  89. if err != nil {
  90. logrus.Infof("parsingError 1: %v\n", err)
  91. return
  92. }
  93. deployServer, ok := extra["deployServ"]
  94. if !ok {
  95. logrus.Infof("parsingError 2: %v\n", extra)
  96. return
  97. }
  98. var ds []string
  99. ds1, ok := deployServer.([]interface{})
  100. if !ok {
  101. logrus.Infof("parsingError 3: %v %T\n", deployServer, deployServer)
  102. return
  103. }
  104. for _, v := range ds1 {
  105. ds = append(ds, v.(string))
  106. }
  107. // err = json.Unmarshal([]byte(ds1), &ds)
  108. // if err != nil {
  109. // logrus.Infof("parsingError 32: %v\n", err)
  110. // return
  111. // }
  112. logrus.Infof("parsingError 4: %v\n", ds)
  113. // 检查进程数量是否正常
  114. var monitor = make(map[string]bool)
  115. // _, startServ, _ := docker.ParseDeployServArgs(deploy.ServerType, ds, deploy.IsMproom)
  116. var serverList []string
  117. if len(ds) == 1 && ds[0] == "all" {
  118. serverList = strings.Split(line, ",")
  119. } else {
  120. serverList = ds
  121. }
  122. for _, v := range serverList {
  123. if v == consts.DeployPackJsonConvert {
  124. continue
  125. }
  126. // if v == consts.DeployPackMPRoom {
  127. // if deploy.IsMproom != consts.INTTRUE {
  128. // continue
  129. // }
  130. // }
  131. n := "./" + v
  132. monitor[n] = false
  133. }
  134. // if consts.DeployTypeGrave == deploy.ServerType {
  135. // if deploy.IsMproom == consts.INTTRUE {
  136. // monitor = map[string]bool{
  137. // "./archive": false,
  138. // // "./chapter": false,
  139. // "./chapterd": false,
  140. // //"./gate": false,
  141. // "./store": false,
  142. // "./world": false,
  143. // "./mproom": false,
  144. // }
  145. // } else {
  146. // monitor = map[string]bool{
  147. // "./archive": false,
  148. // // "./chapter": false,
  149. // "./chapterd": false,
  150. // "./store": false,
  151. // "./world": false,
  152. // }
  153. // }
  154. // }
  155. // if consts.DeployTypeLogin == deploy.ServerType {
  156. // monitor = map[string]bool{
  157. // "./login": false,
  158. // }
  159. // }
  160. // if consts.DeployTypeGate == deploy.ServerType {
  161. // monitor = map[string]bool{
  162. // "./gate": false,
  163. // }
  164. // }
  165. logrus.Infof("parsingError 5: %v\n", monitor)
  166. if len(monitor) == 0 {
  167. logrus.Warn("GameAlarm monitor is not set")
  168. return
  169. }
  170. process, err := docker.GetGraveProcess(docker.GraveProcessInput{Ctx: ctx, Deploy: deploy})
  171. if err != nil {
  172. req.Code = 2
  173. req.Msg = fmt.Sprintf("docker通讯异常:[%v]", err.Error())
  174. return
  175. }
  176. logrus.Infof("parsingError 7: %v\n", string(process))
  177. for _, line := range strings.Split(string(process), "\n") {
  178. // logrus.Infof("line:%s\n", line)
  179. awk := strings.Split(line, " ")
  180. awk2 := awk[len(awk)-1]
  181. if _, ok := monitor[awk2]; ok {
  182. monitor[awk2] = true
  183. }
  184. }
  185. logrus.WithField("method", "Notify").Infof("monitor: %+v\n", monitor)
  186. for s, b := range monitor {
  187. if !b {
  188. ps := strings.ReplaceAll(s, "./", "")
  189. req.Code = 2
  190. req.Msg = fmt.Sprintf("服务启动异常,未监测进程到:[%v]", ps)
  191. return
  192. }
  193. }
  194. }
  195. // 解析日志中是否存在错误
  196. log, err := s.getDeployLog(ctx, forms.DeployLogViewReq{Id: taskItem.ID, LogFile: req.LogFile})
  197. if err != nil {
  198. logrus.Warnf("解析日志失败:%+v", err.Error())
  199. }
  200. parsingError(log)
  201. }
  202. if req.Code != 0 {
  203. status = consts.DeployStatusErr
  204. }
  205. _, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(taskItem.DeployID)).Updates(model.ServerDeploy{
  206. DeployStatus: status,
  207. })
  208. if err != nil {
  209. logrus.Warnf("Updates ServerDeploy err:%+v", err)
  210. return serializer.DBErr(err.Error(), err)
  211. }
  212. _, err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Where(ql.ID.Eq(taskItem.ID)).Updates(model.ServerDeployLog{
  213. LogFile: req.LogFile,
  214. ErrorMsg: req.Msg,
  215. Status: status,
  216. EndAt: time.Now(),
  217. })
  218. if err != nil {
  219. logrus.Warnf("Updates ServerDeployLog err:%+v", err)
  220. return serializer.DBErr(err.Error(), err)
  221. }
  222. msg := fmt.Sprintf("应用[%v]部署成功。远程服务器提示:%v", taskItem.Name, req.Msg)
  223. code := consts.CodeSuccess
  224. if req.Code != 0 {
  225. msg = fmt.Sprintf("应用[%v]部署失败。远程服务器提示:%v", taskItem.Name, req.Msg)
  226. code = consts.CodeParamErr
  227. }
  228. if taskItem.AdminID > 0 {
  229. msgData := ws.Msg{
  230. Id: taskItem.ID,
  231. Type: consts.SocketDeployNotify,
  232. Code: code,
  233. Msg: msg,
  234. }
  235. go ws.SendToAdmin(taskItem.AdminID, msgData)
  236. }
  237. if taskItem.PublishType != consts.DeployPublishTypeVersion {
  238. return serializer.Suc(nil)
  239. }
  240. // 更新统计
  241. if req.Code != 0 {
  242. tx := config.DB.Exec("UPDATE server_deploy_stat SET fail=fail+? WHERE batch_id=?", 1, stat.BatchID)
  243. if tx.Error != nil {
  244. logrus.Warnf("Notify Updates server_deploy_stat fail err:%+v", err)
  245. }
  246. stat.Fail += 1
  247. } else {
  248. tx := config.DB.Exec("UPDATE server_deploy_stat SET success=success+? WHERE batch_id=?", 1, stat.BatchID)
  249. if tx.Error != nil {
  250. logrus.Warnf("Notify Updates server_deploy_stat success err:%+v", err)
  251. }
  252. stat.Success += 1
  253. }
  254. // 满足通知条件
  255. if stat.Success+stat.Fail+stat.Timeout >= stat.Total {
  256. msgData := ws.Msg{
  257. Id: taskItem.ID,
  258. Type: consts.SocketDeployNotify,
  259. Code: consts.CodeSuccess,
  260. Msg: fmt.Sprintf("批量部署任务全部执行完毕,成功:%v,失败:%v,超时:%v", stat.Success, stat.Fail, stat.Timeout),
  261. }
  262. go ws.SendToAdmin(taskItem.AdminID, msgData)
  263. // 更新通知状态
  264. _, err = query.Use(config.DB).ServerDeployStat.WithContext(ctx).Where(qs.ID.Eq(stat.ID)).Updates(model.ServerDeployStat{
  265. IsNotify: consts.DeployNotifyOk,
  266. NotifyAt: time.Now(),
  267. })
  268. if err != nil {
  269. logrus.Warnf("Updates ServerDeployStat err:%+v", err)
  270. return serializer.DBErr(err.Error(), err)
  271. }
  272. }
  273. return serializer.Suc(nil)
  274. }
  275. func extractFromFlagToEnd(input string, startFlag string) (string, error) {
  276. // 使用bufio.NewScanner模拟逐行读取
  277. scanner := bufio.NewScanner(strings.NewReader(input))
  278. var line string
  279. found := false
  280. for scanner.Scan() {
  281. line = scanner.Text()
  282. if strings.Contains(line, startFlag) {
  283. // 找到标记,提取整行
  284. found = true
  285. break
  286. }
  287. }
  288. if err := scanner.Err(); err != nil {
  289. return "", err
  290. }
  291. if !found {
  292. return "", fmt.Errorf("startFlag not found")
  293. }
  294. startIndex := strings.Index(line, startFlag)
  295. if startIndex == -1 {
  296. return "", errors.New("未找到指定的字符串")
  297. }
  298. // 计算截取的开始位置(即"要开始的进程内容:"之后的位置)
  299. // 注意:由于我们要跳过"要开始的进程内容:"这个字符串,所以需要加上它的长度
  300. start := startIndex + len(startFlag)
  301. // 截取字符串
  302. // 这里我们没有指定end,所以它会截取到originalString的末尾
  303. result := line[start:]
  304. return result, nil
  305. }
  306. func (s *sDeploy) getDeployLog(ctx *gin.Context, req forms.DeployLogViewReq) (taskItem *forms.DeployLogViewModel, err error) {
  307. var (
  308. q = query.Use(config.DB).ServerDeploy
  309. ql = query.Use(config.DB).ServerDeployLog
  310. )
  311. if err = ql.Where(ql.ID.Eq(req.Id)).Scan(&taskItem); err != nil {
  312. return
  313. }
  314. if taskItem == nil {
  315. err = errors.New("操作记录不存在")
  316. return
  317. }
  318. deploy, err := q.Where(q.ID.Eq(taskItem.DeployID)).First()
  319. if err != nil {
  320. return
  321. }
  322. if deploy == nil {
  323. err = errors.New("操作记录不存在")
  324. return
  325. }
  326. if taskItem.LogFile == "" && req.LogFile != "" {
  327. taskItem.LogFile = req.LogFile
  328. }
  329. log, err := docker.ReadDeployLog(docker.ReadDeployLogInput{
  330. Ctx: ctx,
  331. Deploy: deploy,
  332. LogFile: taskItem.LogFile,
  333. })
  334. if err != nil {
  335. return
  336. }
  337. taskItem.LogContent = string(log)
  338. taskItem.LogContent = utility.Rmu0000(taskItem.LogContent)
  339. taskItem.LogContent = strings.Replace(taskItem.LogContent, ``, "", -1)
  340. taskItem.LogContent = strings.Replace(taskItem.LogContent, `INFO`, "", -1)
  341. return
  342. }
  343. func (s *sDeploy) LogView(ctx *gin.Context, req forms.DeployLogViewReq) serializer.Response {
  344. taskItem, err := s.getDeployLog(ctx, req)
  345. if err != nil {
  346. return serializer.ParamErr(err.Error(), err)
  347. }
  348. return serializer.Suc(taskItem)
  349. }
  350. func (s *sDeploy) LogList(ctx *gin.Context, req forms.DeployLogListReq) serializer.Response {
  351. var (
  352. q = query.Use(config.DB).ServerDeployLog
  353. m = q.WithContext(ctx).Where(q.DeployID.Eq(req.DeployId))
  354. offset int64 = 0
  355. models forms.UserAccountListRes
  356. lists []forms.DeployLogListModel
  357. )
  358. m = m.Order(q.ID.Desc())
  359. req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)
  360. count, err := m.Count()
  361. if err != nil {
  362. return serializer.Err(consts.CodeParamErr, "查询出错 count", err)
  363. }
  364. if count > 0 {
  365. if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil {
  366. return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
  367. }
  368. }
  369. for k, v := range lists {
  370. if v.AdminID > 0 {
  371. user, err := User.GetUser(v.AdminID)
  372. if err != nil {
  373. logrus.Warnf("Deploy LogList GetUser err:%+v", err)
  374. continue
  375. }
  376. if user != nil {
  377. lists[k].AdminName = fmt.Sprintf("%v(%v)", user.UserName, v.AdminID)
  378. }
  379. }
  380. }
  381. models.List = lists
  382. models.Page = req.Page
  383. models.PerPage = req.PerPage
  384. models.PageCount = (count + req.PerPage - 1) / req.PerPage
  385. return serializer.Suc(models)
  386. }
  387. // 获取部署的服务器列表
  388. func (s *sDeploy) List(ctx *gin.Context, req forms.DeployListReq) serializer.Response {
  389. var (
  390. q = query.Use(config.DB).ServerDeploy
  391. m = q.WithContext(ctx).Where(q.Environment.Eq(os.Getenv("GIN_MODE")))
  392. offset int64 = 0
  393. models forms.UserAccountListRes
  394. lists []forms.DeployListData
  395. )
  396. if req.Name != "" {
  397. m = m.Where(q.Name.Like(req.Name))
  398. }
  399. if req.ContainerName != "" {
  400. m = m.Where(q.ContainerName.Like(req.ContainerName))
  401. }
  402. if len(req.ServerType) > 0 {
  403. m = m.Where(q.ServerType.In(req.ServerType...))
  404. }
  405. m = m.Order(q.ServerType, q.ID.Desc())
  406. req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)
  407. count, err := m.Count()
  408. if err != nil {
  409. return serializer.Err(consts.CodeParamErr, "查询出错 count", err)
  410. }
  411. if count > 0 {
  412. //if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil {
  413. if err = m.Scan(&lists); err != nil {
  414. return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
  415. }
  416. }
  417. //区分多少docker地址
  418. dockerMap := make(map[string]map[string]string)
  419. for _, item := range lists {
  420. if _, ok := dockerMap[item.DockerAddr]; ok {
  421. continue
  422. }
  423. dockerInfoMap := make(map[string]string)
  424. dockerInfoMap["addr"] = item.DockerAddr
  425. dockerInfoMap["caPem"] = item.CaPem
  426. dockerInfoMap["certPem"] = item.CertPem
  427. dockerInfoMap["keyPem"] = item.KeyPem
  428. dockerMap[item.DockerAddr] = dockerInfoMap
  429. }
  430. logrus.Infof("dockerMap:%+v", dockerMap)
  431. //获取docker列表
  432. containerMap := make(map[string]types.Container)
  433. for addr, info := range dockerMap {
  434. logrus.Infof("info:%+v", info)
  435. containerList, err := docker.GetContainerList(ctx, info["addr"], info["caPem"], info["certPem"], info["keyPem"])
  436. if err != nil {
  437. logrus.Errorf("获取%s docker列表失败:%v", addr, err)
  438. // return serializer.Response{}
  439. }
  440. for _, item := range containerList {
  441. containerMap[item.Names[0]] = item
  442. }
  443. }
  444. nt := time.Now()
  445. for k, v := range lists {
  446. if v.AdminID > 0 {
  447. user, err := User.GetUser(v.AdminID)
  448. if err != nil {
  449. logrus.Warnf("Deploy LogList GetUser err:%+v", err)
  450. continue
  451. }
  452. if user != nil {
  453. lists[k].AdminName = fmt.Sprintf("%v(%v)", user.UserName, v.AdminID)
  454. }
  455. }
  456. if v.LastSyncAt.Add(consts.DeployRunSyncTime).Before(nt) {
  457. runStatus := consts.DeployRunStart
  458. container := containerMap["/"+v.ContainerName]
  459. if container.State != "running" {
  460. runStatus = consts.DeployRunStop
  461. }
  462. if container.ID == "" {
  463. runStatus = consts.DeployRunNoFound
  464. }
  465. _, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(v.ID)).Updates(model.ServerDeploy{
  466. RunStatus: runStatus,
  467. LastSyncAt: time.Now(),
  468. })
  469. if err != nil {
  470. logrus.Warnf("ServerDeploy Updates LastSyncAt err:%+v", err)
  471. }
  472. }
  473. lists[k].DeployStatus = s.getLastDeployStatus(ctx, v.ID)
  474. }
  475. //for k, v := range lists {
  476. // if v.AdminID > 0 {
  477. // user, err := User.GetUser(v.AdminID)
  478. // if err != nil {
  479. // logrus.Warnf("Deploy LogList GetUser err:%+v", err)
  480. // continue
  481. // }
  482. // if user != nil {
  483. // lists[k].AdminName = fmt.Sprintf("%v(%v)", user.UserName, v.AdminID)
  484. // }
  485. // }
  486. // if v.LastSyncAt.Add(consts.DeployRunSyncTime).Before(nt) {
  487. // runStatus := consts.DeployRunStart
  488. // container, err := docker.GetContainer(ctx, v.DockerAddr, v.CaPem, v.CertPem, v.KeyPem, v.ContainerName)
  489. // if err != nil {
  490. // logrus.Warnf("ContainerErr: %+v", err)
  491. // }
  492. //
  493. // if container != nil {
  494. // if container.State != "running" {
  495. // runStatus = consts.DeployRunStop
  496. // }
  497. // } else {
  498. // runStatus = consts.DeployRunNoFound
  499. // logrus.Warnf("ContainerInfo: %+v;\n", container)
  500. // }
  501. //
  502. // _, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(v.ID)).Updates(model.ServerDeploy{
  503. // RunStatus: runStatus,
  504. // LastSyncAt: time.Now(),
  505. // })
  506. //
  507. // if err != nil {
  508. // logrus.Warnf("ServerDeploy Updates LastSyncAt err:%+v", err)
  509. // }
  510. //
  511. // }
  512. //
  513. // lists[k].DeployStatus = s.getLastDeployStatus(ctx, v.ID)
  514. //}
  515. newList1 := make([]forms.DeployListData, 0)
  516. if len(req.RunStatus) > 0 {
  517. for _, item := range lists {
  518. if character.InSlice(req.RunStatus, strconv.Itoa(int(item.RunStatus))) {
  519. newList1 = append(newList1, item)
  520. }
  521. }
  522. } else {
  523. newList1 = lists
  524. }
  525. newList2 := make([]forms.DeployListData, 0)
  526. if len(req.DeployStatus) > 0 {
  527. for _, item := range newList1 {
  528. if character.InSlice(req.DeployStatus, strconv.Itoa(int(item.DeployStatus))) {
  529. newList2 = append(newList2, item)
  530. }
  531. }
  532. } else {
  533. newList2 = newList1
  534. }
  535. pageCount := int64(len(newList2))
  536. pageList := make([]forms.DeployListData, 0)
  537. if offset+req.PerPage <= pageCount-1 {
  538. pageList = newList2[offset : req.PerPage+offset]
  539. } else if offset+req.PerPage > pageCount-1 && offset <= pageCount-1 {
  540. pageList = newList2[offset:]
  541. }
  542. models.List = pageList
  543. //models.List = lists
  544. models.Page = req.Page
  545. models.PerPage = req.PerPage
  546. // todo 分页逻辑处理
  547. models.PageCount = (pageCount + req.PerPage - 1) / req.PerPage
  548. return serializer.Suc(models)
  549. }
  550. // getLastDeployStatus 获取最后的部署状态
  551. func (s *sDeploy) getLastDeployStatus(ctx *gin.Context, deployId int64) int32 {
  552. var ql = query.Use(config.DB).ServerDeployLog
  553. log, _ := ql.WithContext(ctx).Where(ql.DeployID.Eq(deployId)).Order(ql.ID.Desc()).First()
  554. if log == nil {
  555. return consts.DeployStatusWait
  556. }
  557. return log.Status
  558. }
  559. // 下发一键重启等部署任务
  560. func (s *sDeploy) Task(ctx *gin.Context, req forms.DeployTaskReq) serializer.Response {
  561. var (
  562. batchId = character.Md5Content([]byte(character.RandStringRunes(32)))
  563. taskList = make(map[string]docker.PushDeployTaskInput, len(req.Ids))
  564. )
  565. err := config.DB.Transaction(func(tx *gorm.DB) error {
  566. for _, id := range req.Ids {
  567. var (
  568. q = query.Use(config.DB).ServerDeploy
  569. ql = query.Use(config.DB).ServerDeployLog
  570. err error
  571. )
  572. first, err := q.Where(q.ID.Eq(id)).First()
  573. if err != nil {
  574. return err
  575. }
  576. if first == nil {
  577. return errors.New(fmt.Sprintf("服务器不存在 id:%v", id))
  578. }
  579. log, _ := ql.Where(ql.DeployID.Eq(first.ID)).Order(ql.CreatedAt.Desc()).First()
  580. // 只要正式服验证时间
  581. if os.Getenv("GIN_MODE") == "release" && log != nil && log.CreatedAt.Unix()+30 > time.Now().Unix() {
  582. return errors.New(fmt.Sprintf("距离上次部署时间过近,请稍后%v秒后再试 id:%v", log.CreatedAt.Unix()+30-time.Now().Unix(), id))
  583. }
  584. extra := make(map[string]interface{})
  585. extra["deployServ"] = req.DeployServ
  586. strExtra, err := json.Marshal(extra)
  587. if err != nil {
  588. return err
  589. }
  590. // 新建部署任务记录
  591. createData := &model.ServerDeployLog{
  592. AdminID: token.GetUID(ctx),
  593. DeployID: first.ID,
  594. BatchID: batchId,
  595. TraceID: character.Md5Content([]byte(character.RandStringRunes(32))),
  596. ContainerName: first.ContainerName,
  597. Name: first.Name,
  598. PublishType: consts.DeployPublishTypeVersion,
  599. Version: req.Version,
  600. Extra: string(strExtra),
  601. LogFile: "",
  602. Status: consts.DeployStatusIng,
  603. EndAt: time.Now().AddDate(-20, 0, 0),
  604. CreatedAt: time.Now(),
  605. }
  606. if err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Create(createData); err != nil {
  607. return err
  608. }
  609. if createData.ID < 1 {
  610. return errors.New(fmt.Sprintf("创建失败,请稍后重试! id:%v", id))
  611. }
  612. logrus.Warnf("DeployTask createData:%+v", createData)
  613. taskList[createData.TraceID] = docker.PushDeployTaskInput{
  614. Ctx: ctx,
  615. Deploy: first,
  616. Log: createData,
  617. DeployServ: req.DeployServ,
  618. }
  619. }
  620. // 插入批量记录
  621. if err := query.Use(config.DB).ServerDeployStat.WithContext(ctx).Create(&model.ServerDeployStat{
  622. AdminID: token.GetUID(ctx),
  623. BatchID: batchId,
  624. Total: int64(len(req.Ids)),
  625. Success: 0,
  626. Fail: 0,
  627. Timeout: 0,
  628. NotifyAt: time.Now().AddDate(-20, 0, 0),
  629. IsNotify: consts.DeployNotifyNo,
  630. CreatedAt: time.Now(),
  631. }); err != nil {
  632. return err
  633. }
  634. return nil
  635. })
  636. if err != nil {
  637. return serializer.DBErr(err.Error(), err)
  638. }
  639. for _, task := range taskList {
  640. err = docker.PushDeployTask(task)
  641. if err != nil {
  642. logrus.Infof("task.PushDeployTask return err:%+v", err)
  643. var (
  644. q = query.Use(config.DB).ServerDeploy
  645. ql = query.Use(config.DB).ServerDeployLog
  646. )
  647. _, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(task.Deploy.ID)).Updates(model.ServerDeploy{
  648. RunStatus: consts.DeployStatusErr,
  649. LastSyncAt: time.Now(),
  650. })
  651. if err != nil {
  652. logrus.Warnf("task.ServerDeploy Updates LastSyncAt err:%+v", err)
  653. }
  654. errorMsg := fmt.Sprintf("创建远程任务失败:%v ", err.Error())
  655. _, err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Where(ql.ID.Eq(task.Log.ID)).Updates(model.ServerDeployLog{
  656. ErrorMsg: errorMsg,
  657. Status: consts.DeployStatusErr,
  658. EndAt: time.Now(),
  659. })
  660. if err != nil {
  661. logrus.Warnf("task.ServerDeployLog Updates LastSyncAt err:%+v", err)
  662. }
  663. tx := config.DB.Exec("UPDATE server_deploy_stat SET fail=fail+? WHERE batch_id=?", 1, batchId)
  664. if tx.Error != nil {
  665. logrus.Warnf("task.ServerDeployLog Updates server_deploy_stat err:%+v", err)
  666. }
  667. // 推送失败通知
  668. if task.Log.AdminID > 0 {
  669. msgData := ws.Msg{
  670. Id: task.Deploy.ID,
  671. Type: consts.SocketDeployNotify,
  672. Code: consts.CodeParamErr,
  673. Msg: fmt.Sprintf("应用[%v]部署失败。远程服务器提示:%v", task.Deploy.Name, errorMsg),
  674. }
  675. go ws.SendToAdmin(task.Log.AdminID, msgData)
  676. }
  677. }
  678. }
  679. return serializer.Suc(nil)
  680. }
  681. func (s *sDeploy) Stop(ctx *gin.Context, req forms.DeployStopReq) serializer.Response {
  682. for _, id := range req.Ids {
  683. var (
  684. q = query.Use(config.DB).ServerDeploy
  685. ql = query.Use(config.DB).ServerDeployLog
  686. err error
  687. )
  688. first, err := q.Where(q.ID.Eq(id)).First()
  689. if err != nil {
  690. return serializer.DBErr(err.Error(), err)
  691. }
  692. if first == nil {
  693. return serializer.DBErr(fmt.Sprintf("Stop 服务器不存在 id:%v", id), nil)
  694. }
  695. log, _ := ql.Where(ql.DeployID.Eq(first.ID)).Order(ql.CreatedAt.Desc()).First()
  696. if log != nil && log.CreatedAt.Unix()+30 > time.Now().Unix() {
  697. return serializer.DBErr(fmt.Sprintf("Stop 距离上次部署时间过近,请稍后%v秒后再试 id:%v", log.CreatedAt.Unix()+30-time.Now().Unix(), id), nil)
  698. }
  699. // 新建部署任务记录
  700. createData := &model.ServerDeployLog{
  701. AdminID: token.GetUID(ctx),
  702. DeployID: first.ID,
  703. TraceID: character.Md5Content([]byte(character.RandStringRunes(32))),
  704. ContainerName: first.ContainerName,
  705. Name: first.Name,
  706. PublishType: consts.DeployPublishTypeStop,
  707. Extra: fmt.Sprintf(`{"stopType":%v}`, req.StopType),
  708. LogFile: "",
  709. Status: consts.DeployStatusIng,
  710. EndAt: time.Now(),
  711. CreatedAt: time.Now(),
  712. }
  713. if err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Create(createData); err != nil {
  714. return serializer.DBErr(err.Error(), err)
  715. }
  716. if createData.ID < 1 {
  717. return serializer.ParamErr(fmt.Sprintf("Stop 创建失败,请稍后重试! id:%v", id), nil)
  718. }
  719. err = docker.PushDeployStop(docker.PushDeployStopInput{
  720. Ctx: ctx,
  721. Deploy: first,
  722. Log: createData,
  723. StopType: req.StopType,
  724. })
  725. if err != nil {
  726. return serializer.ParamErr(fmt.Sprintf("Stop 创建远程任务失败:%v id:%v", err.Error(), id), nil)
  727. }
  728. }
  729. return serializer.Suc(nil)
  730. }
  731. // 编辑某服的配置
  732. func (s *sDeploy) Edit(ctx *gin.Context, req forms.DeployEditReq) serializer.Response {
  733. var (
  734. q = query.Use(config.DB).ServerDeploy
  735. err error
  736. )
  737. // 编辑
  738. if req.ID > 0 {
  739. _, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(req.ID)).Updates(model.ServerDeploy{
  740. GroupID: req.GroupID,
  741. ServerType: req.ServerType,
  742. ContainerName: req.ContainerName,
  743. Name: req.Name,
  744. DockerAddr: req.DockerAddr,
  745. CaPem: req.CaPem,
  746. CertPem: req.CertPem,
  747. KeyPem: req.KeyPem,
  748. Remark: req.Remark,
  749. IsMproom: req.IsMproom,
  750. LastSyncAt: time.Time{},
  751. CreatedAt: time.Now(),
  752. })
  753. if err != nil {
  754. return serializer.DBErr(err.Error(), err)
  755. }
  756. return serializer.Suc(nil)
  757. }
  758. // 新增
  759. createData := model.ServerDeploy{
  760. Environment: os.Getenv("GIN_MODE"),
  761. AdminID: token.GetUID(ctx),
  762. GroupID: req.GroupID,
  763. ContainerName: req.ContainerName,
  764. Name: req.Name,
  765. ServerType: req.ServerType,
  766. DockerAddr: req.DockerAddr,
  767. CaPem: req.CaPem,
  768. CertPem: req.CertPem,
  769. KeyPem: req.KeyPem,
  770. Remark: req.Remark,
  771. RunStatus: consts.DeployRunStop,
  772. DeployStatus: consts.DeployStatusWait,
  773. CreatedAt: time.Now(),
  774. }
  775. if err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Omit(q.LastSyncAt.Desc()).Create(&createData); err != nil {
  776. return serializer.DBErr(err.Error(), err)
  777. }
  778. if createData.ID < 1 {
  779. return serializer.ParamErr("创建失败,请稍后重试!", nil)
  780. }
  781. return serializer.Suc(nil)
  782. }
  783. func (s *sDeploy) Delete(ctx *gin.Context, req forms.DeployDeleteReq) serializer.Response {
  784. // 编辑
  785. if req.ID <= 0 {
  786. return serializer.ParamErr("ID不能空,删除失败!", nil)
  787. }
  788. if _, err := query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(query.Use(config.DB).ServerDeploy.ID.Eq(req.ID)).Delete(); err != nil {
  789. return serializer.DBErr(err.Error(), err)
  790. }
  791. return serializer.Suc(nil)
  792. }