gitea源码

clear_tasks.go 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package actions
  4. import (
  5. "context"
  6. "fmt"
  7. "time"
  8. actions_model "code.gitea.io/gitea/models/actions"
  9. "code.gitea.io/gitea/models/db"
  10. repo_model "code.gitea.io/gitea/models/repo"
  11. "code.gitea.io/gitea/modules/actions"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/timeutil"
  15. webhook_module "code.gitea.io/gitea/modules/webhook"
  16. notify_service "code.gitea.io/gitea/services/notify"
  17. )
  18. // StopZombieTasks stops the task which have running status, but haven't been updated for a long time
  19. func StopZombieTasks(ctx context.Context) error {
  20. return stopTasks(ctx, actions_model.FindTaskOptions{
  21. Status: actions_model.StatusRunning,
  22. UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.ZombieTaskTimeout).Unix()),
  23. })
  24. }
  25. // StopEndlessTasks stops the tasks which have running status and continuous updates, but don't end for a long time
  26. func StopEndlessTasks(ctx context.Context) error {
  27. return stopTasks(ctx, actions_model.FindTaskOptions{
  28. Status: actions_model.StatusRunning,
  29. StartedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.EndlessTaskTimeout).Unix()),
  30. })
  31. }
  32. func notifyWorkflowJobStatusUpdate(ctx context.Context, jobs []*actions_model.ActionRunJob) {
  33. if len(jobs) > 0 {
  34. CreateCommitStatus(ctx, jobs...)
  35. for _, job := range jobs {
  36. _ = job.LoadAttributes(ctx)
  37. notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
  38. }
  39. job := jobs[0]
  40. notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
  41. }
  42. }
  43. func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
  44. jobs, err := actions_model.CancelPreviousJobs(ctx, repoID, ref, workflowID, event)
  45. notifyWorkflowJobStatusUpdate(ctx, jobs)
  46. return err
  47. }
  48. func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
  49. jobs, err := actions_model.CleanRepoScheduleTasks(ctx, repo)
  50. notifyWorkflowJobStatusUpdate(ctx, jobs)
  51. return err
  52. }
  53. func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
  54. tasks, err := db.Find[actions_model.ActionTask](ctx, opts)
  55. if err != nil {
  56. return fmt.Errorf("find tasks: %w", err)
  57. }
  58. jobs := make([]*actions_model.ActionRunJob, 0, len(tasks))
  59. for _, task := range tasks {
  60. if err := db.WithTx(ctx, func(ctx context.Context) error {
  61. if err := actions_model.StopTask(ctx, task.ID, actions_model.StatusFailure); err != nil {
  62. return err
  63. }
  64. if err := task.LoadJob(ctx); err != nil {
  65. return err
  66. }
  67. jobs = append(jobs, task.Job)
  68. return nil
  69. }); err != nil {
  70. log.Warn("Cannot stop task %v: %v", task.ID, err)
  71. continue
  72. }
  73. remove, err := actions.TransferLogs(ctx, task.LogFilename)
  74. if err != nil {
  75. log.Warn("Cannot transfer logs of task %v: %v", task.ID, err)
  76. continue
  77. }
  78. task.LogInStorage = true
  79. if err := actions_model.UpdateTask(ctx, task, "log_in_storage"); err != nil {
  80. log.Warn("Cannot update task %v: %v", task.ID, err)
  81. continue
  82. }
  83. remove()
  84. }
  85. notifyWorkflowJobStatusUpdate(ctx, jobs)
  86. return nil
  87. }
  88. // CancelAbandonedJobs cancels jobs that have not been picked by any runner for a long time
  89. func CancelAbandonedJobs(ctx context.Context) error {
  90. jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{
  91. Statuses: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusBlocked},
  92. UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()),
  93. })
  94. if err != nil {
  95. log.Warn("find abandoned tasks: %v", err)
  96. return err
  97. }
  98. now := timeutil.TimeStampNow()
  99. // Collect one job per run to send workflow run status update
  100. updatedRuns := map[int64]*actions_model.ActionRunJob{}
  101. for _, job := range jobs {
  102. job.Status = actions_model.StatusCancelled
  103. job.Stopped = now
  104. updated := false
  105. if err := db.WithTx(ctx, func(ctx context.Context) error {
  106. n, err := actions_model.UpdateRunJob(ctx, job, nil, "status", "stopped")
  107. if err != nil {
  108. return err
  109. }
  110. if err := job.LoadAttributes(ctx); err != nil {
  111. return err
  112. }
  113. updated = n > 0
  114. if updated && job.Run.Status.IsDone() {
  115. updatedRuns[job.RunID] = job
  116. }
  117. return nil
  118. }); err != nil {
  119. log.Warn("cancel abandoned job %v: %v", job.ID, err)
  120. // go on
  121. }
  122. CreateCommitStatus(ctx, job)
  123. if updated {
  124. notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
  125. }
  126. }
  127. for _, job := range updatedRuns {
  128. notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
  129. }
  130. return nil
  131. }