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.

runner.go 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package runner
  4. import (
  5. "context"
  6. "errors"
  7. "net/http"
  8. actions_model "code.gitea.io/gitea/models/actions"
  9. "code.gitea.io/gitea/modules/actions"
  10. "code.gitea.io/gitea/modules/json"
  11. "code.gitea.io/gitea/modules/log"
  12. actions_service "code.gitea.io/gitea/services/actions"
  13. runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
  14. "code.gitea.io/actions-proto-go/runner/v1/runnerv1connect"
  15. "github.com/bufbuild/connect-go"
  16. gouuid "github.com/google/uuid"
  17. "google.golang.org/grpc/codes"
  18. "google.golang.org/grpc/status"
  19. )
  20. func NewRunnerServiceHandler() (string, http.Handler) {
  21. return runnerv1connect.NewRunnerServiceHandler(
  22. &Service{},
  23. connect.WithCompressMinBytes(1024),
  24. withRunner,
  25. )
  26. }
  27. var _ runnerv1connect.RunnerServiceClient = (*Service)(nil)
  28. type Service struct {
  29. runnerv1connect.UnimplementedRunnerServiceHandler
  30. }
  31. // Register for new runner.
  32. func (s *Service) Register(
  33. ctx context.Context,
  34. req *connect.Request[runnerv1.RegisterRequest],
  35. ) (*connect.Response[runnerv1.RegisterResponse], error) {
  36. if req.Msg.Token == "" || req.Msg.Name == "" {
  37. return nil, errors.New("missing runner token, name")
  38. }
  39. runnerToken, err := actions_model.GetRunnerToken(ctx, req.Msg.Token)
  40. if err != nil {
  41. return nil, errors.New("runner token not found")
  42. }
  43. if runnerToken.IsActive {
  44. return nil, errors.New("runner token has already activated")
  45. }
  46. // create new runner
  47. runner := &actions_model.ActionRunner{
  48. UUID: gouuid.New().String(),
  49. Name: req.Msg.Name,
  50. OwnerID: runnerToken.OwnerID,
  51. RepoID: runnerToken.RepoID,
  52. AgentLabels: req.Msg.AgentLabels,
  53. CustomLabels: req.Msg.CustomLabels,
  54. }
  55. if err := runner.GenerateToken(); err != nil {
  56. return nil, errors.New("can't generate token")
  57. }
  58. // create new runner
  59. if err := actions_model.CreateRunner(ctx, runner); err != nil {
  60. return nil, errors.New("can't create new runner")
  61. }
  62. // update token status
  63. runnerToken.IsActive = true
  64. if err := actions_model.UpdateRunnerToken(ctx, runnerToken, "is_active"); err != nil {
  65. return nil, errors.New("can't update runner token status")
  66. }
  67. res := connect.NewResponse(&runnerv1.RegisterResponse{
  68. Runner: &runnerv1.Runner{
  69. Id: runner.ID,
  70. Uuid: runner.UUID,
  71. Token: runner.Token,
  72. Name: runner.Name,
  73. AgentLabels: runner.AgentLabels,
  74. CustomLabels: runner.CustomLabels,
  75. },
  76. })
  77. return res, nil
  78. }
  79. // FetchTask assigns a task to the runner
  80. func (s *Service) FetchTask(
  81. ctx context.Context,
  82. req *connect.Request[runnerv1.FetchTaskRequest],
  83. ) (*connect.Response[runnerv1.FetchTaskResponse], error) {
  84. runner := GetRunner(ctx)
  85. var task *runnerv1.Task
  86. if t, ok, err := pickTask(ctx, runner); err != nil {
  87. log.Error("pick task failed: %v", err)
  88. return nil, status.Errorf(codes.Internal, "pick task: %v", err)
  89. } else if ok {
  90. task = t
  91. }
  92. res := connect.NewResponse(&runnerv1.FetchTaskResponse{
  93. Task: task,
  94. })
  95. return res, nil
  96. }
  97. // UpdateTask updates the task status.
  98. func (s *Service) UpdateTask(
  99. ctx context.Context,
  100. req *connect.Request[runnerv1.UpdateTaskRequest],
  101. ) (*connect.Response[runnerv1.UpdateTaskResponse], error) {
  102. {
  103. // to debug strange runner behaviors, it could be removed if all problems have been solved.
  104. stateMsg, _ := json.Marshal(req.Msg.State)
  105. log.Trace("update task with state: %s", stateMsg)
  106. }
  107. // Get Task first
  108. task, err := actions_model.GetTaskByID(ctx, req.Msg.State.Id)
  109. if err != nil {
  110. return nil, status.Errorf(codes.Internal, "can't find the task: %v", err)
  111. }
  112. if task.Status.IsCancelled() {
  113. return connect.NewResponse(&runnerv1.UpdateTaskResponse{
  114. State: &runnerv1.TaskState{
  115. Id: req.Msg.State.Id,
  116. Result: task.Status.AsResult(),
  117. },
  118. }), nil
  119. }
  120. task, err = actions_model.UpdateTaskByState(ctx, req.Msg.State)
  121. if err != nil {
  122. return nil, status.Errorf(codes.Internal, "update task: %v", err)
  123. }
  124. if err := task.LoadJob(ctx); err != nil {
  125. return nil, status.Errorf(codes.Internal, "load job: %v", err)
  126. }
  127. if err := actions_service.CreateCommitStatus(ctx, task.Job); err != nil {
  128. log.Error("Update commit status failed: %v", err)
  129. // go on
  130. }
  131. if req.Msg.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
  132. if err := actions_service.EmitJobsIfReady(task.Job.RunID); err != nil {
  133. log.Error("Emit ready jobs of run %d: %v", task.Job.RunID, err)
  134. }
  135. }
  136. return connect.NewResponse(&runnerv1.UpdateTaskResponse{
  137. State: &runnerv1.TaskState{
  138. Id: req.Msg.State.Id,
  139. Result: task.Status.AsResult(),
  140. },
  141. }), nil
  142. }
  143. // UpdateLog uploads log of the task.
  144. func (s *Service) UpdateLog(
  145. ctx context.Context,
  146. req *connect.Request[runnerv1.UpdateLogRequest],
  147. ) (*connect.Response[runnerv1.UpdateLogResponse], error) {
  148. res := connect.NewResponse(&runnerv1.UpdateLogResponse{})
  149. task, err := actions_model.GetTaskByID(ctx, req.Msg.TaskId)
  150. if err != nil {
  151. return nil, status.Errorf(codes.Internal, "get task: %v", err)
  152. }
  153. ack := task.LogLength
  154. if len(req.Msg.Rows) == 0 || req.Msg.Index > ack || int64(len(req.Msg.Rows))+req.Msg.Index <= ack {
  155. res.Msg.AckIndex = ack
  156. return res, nil
  157. }
  158. if task.LogInStorage {
  159. return nil, status.Errorf(codes.AlreadyExists, "log file has been archived")
  160. }
  161. rows := req.Msg.Rows[ack-req.Msg.Index:]
  162. ns, err := actions.WriteLogs(ctx, task.LogFilename, task.LogSize, rows)
  163. if err != nil {
  164. return nil, status.Errorf(codes.Internal, "write logs: %v", err)
  165. }
  166. task.LogLength += int64(len(rows))
  167. for _, n := range ns {
  168. task.LogIndexes = append(task.LogIndexes, task.LogSize)
  169. task.LogSize += int64(n)
  170. }
  171. res.Msg.AckIndex = task.LogLength
  172. var remove func()
  173. if req.Msg.NoMore {
  174. task.LogInStorage = true
  175. remove, err = actions.TransferLogs(ctx, task.LogFilename)
  176. if err != nil {
  177. return nil, status.Errorf(codes.Internal, "transfer logs: %v", err)
  178. }
  179. }
  180. if err := actions_model.UpdateTask(ctx, task, "log_indexes", "log_length", "log_size", "log_in_storage"); err != nil {
  181. return nil, status.Errorf(codes.Internal, "update task: %v", err)
  182. }
  183. if remove != nil {
  184. remove()
  185. }
  186. return res, nil
  187. }