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.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package actions
  4. import (
  5. "context"
  6. "fmt"
  7. "strings"
  8. "time"
  9. "code.gitea.io/gitea/models/db"
  10. repo_model "code.gitea.io/gitea/models/repo"
  11. user_model "code.gitea.io/gitea/models/user"
  12. "code.gitea.io/gitea/modules/timeutil"
  13. "code.gitea.io/gitea/modules/translation"
  14. "code.gitea.io/gitea/modules/util"
  15. runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
  16. "xorm.io/builder"
  17. )
  18. // ActionRunner represents runner machines
  19. type ActionRunner struct {
  20. ID int64
  21. UUID string `xorm:"CHAR(36) UNIQUE"`
  22. Name string `xorm:"VARCHAR(255)"`
  23. OwnerID int64 `xorm:"index"` // org level runner, 0 means system
  24. Owner *user_model.User `xorm:"-"`
  25. RepoID int64 `xorm:"index"` // repo level runner, if orgid also is zero, then it's a global
  26. Repo *repo_model.Repository `xorm:"-"`
  27. Description string `xorm:"TEXT"`
  28. Base int // 0 native 1 docker 2 virtual machine
  29. RepoRange string // glob match which repositories could use this runner
  30. Token string `xorm:"-"`
  31. TokenHash string `xorm:"UNIQUE"` // sha256 of token
  32. TokenSalt string
  33. // TokenLastEight string `xorm:"token_last_eight"` // it's unnecessary because we don't find runners by token
  34. LastOnline timeutil.TimeStamp `xorm:"index"`
  35. LastActive timeutil.TimeStamp `xorm:"index"`
  36. // Store OS and Artch.
  37. AgentLabels []string
  38. // Store custom labes use defined.
  39. CustomLabels []string
  40. Created timeutil.TimeStamp `xorm:"created"`
  41. Updated timeutil.TimeStamp `xorm:"updated"`
  42. Deleted timeutil.TimeStamp `xorm:"deleted"`
  43. }
  44. func (r *ActionRunner) OwnType() string {
  45. if r.RepoID != 0 {
  46. return fmt.Sprintf("Repo(%s)", r.Repo.FullName())
  47. }
  48. if r.OwnerID != 0 {
  49. return fmt.Sprintf("Org(%s)", r.Owner.Name)
  50. }
  51. return "Global"
  52. }
  53. func (r *ActionRunner) Status() runnerv1.RunnerStatus {
  54. if time.Since(r.LastOnline.AsTime()) > time.Minute {
  55. return runnerv1.RunnerStatus_RUNNER_STATUS_OFFLINE
  56. }
  57. if time.Since(r.LastActive.AsTime()) > 10*time.Second {
  58. return runnerv1.RunnerStatus_RUNNER_STATUS_IDLE
  59. }
  60. return runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE
  61. }
  62. func (r *ActionRunner) StatusName() string {
  63. return strings.ToLower(strings.TrimPrefix(r.Status().String(), "RUNNER_STATUS_"))
  64. }
  65. func (r *ActionRunner) StatusLocaleName(lang translation.Locale) string {
  66. return lang.Tr("actions.runners.status." + r.StatusName())
  67. }
  68. func (r *ActionRunner) IsOnline() bool {
  69. status := r.Status()
  70. if status == runnerv1.RunnerStatus_RUNNER_STATUS_IDLE || status == runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE {
  71. return true
  72. }
  73. return false
  74. }
  75. // AllLabels returns agent and custom labels
  76. func (r *ActionRunner) AllLabels() []string {
  77. return append(r.AgentLabels, r.CustomLabels...)
  78. }
  79. // Editable checks if the runner is editable by the user
  80. func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
  81. if ownerID == 0 && repoID == 0 {
  82. return true
  83. }
  84. if ownerID > 0 && r.OwnerID == ownerID {
  85. return true
  86. }
  87. return repoID > 0 && r.RepoID == repoID
  88. }
  89. // LoadAttributes loads the attributes of the runner
  90. func (r *ActionRunner) LoadAttributes(ctx context.Context) error {
  91. if r.OwnerID > 0 {
  92. var user user_model.User
  93. has, err := db.GetEngine(ctx).ID(r.OwnerID).Get(&user)
  94. if err != nil {
  95. return err
  96. }
  97. if has {
  98. r.Owner = &user
  99. }
  100. }
  101. if r.RepoID > 0 {
  102. var repo repo_model.Repository
  103. has, err := db.GetEngine(ctx).ID(r.RepoID).Get(&repo)
  104. if err != nil {
  105. return err
  106. }
  107. if has {
  108. r.Repo = &repo
  109. }
  110. }
  111. return nil
  112. }
  113. func (r *ActionRunner) GenerateToken() (err error) {
  114. r.Token, r.TokenSalt, r.TokenHash, _, err = generateSaltedToken()
  115. return err
  116. }
  117. func init() {
  118. db.RegisterModel(&ActionRunner{})
  119. }
  120. type FindRunnerOptions struct {
  121. db.ListOptions
  122. RepoID int64
  123. OwnerID int64
  124. Sort string
  125. Filter string
  126. WithAvailable bool // not only runners belong to, but also runners can be used
  127. }
  128. func (opts FindRunnerOptions) toCond() builder.Cond {
  129. cond := builder.NewCond()
  130. if opts.RepoID > 0 {
  131. c := builder.NewCond().And(builder.Eq{"repo_id": opts.RepoID})
  132. if opts.WithAvailable {
  133. c = c.Or(builder.Eq{"owner_id": builder.Select("owner_id").From("repository").Where(builder.Eq{"id": opts.RepoID})})
  134. c = c.Or(builder.Eq{"repo_id": 0, "owner_id": 0})
  135. }
  136. cond = cond.And(c)
  137. }
  138. if opts.OwnerID > 0 {
  139. c := builder.NewCond().And(builder.Eq{"owner_id": opts.OwnerID})
  140. if opts.WithAvailable {
  141. c = c.Or(builder.Eq{"repo_id": 0, "owner_id": 0})
  142. }
  143. cond = cond.And(c)
  144. }
  145. if opts.Filter != "" {
  146. cond = cond.And(builder.Like{"name", opts.Filter})
  147. }
  148. return cond
  149. }
  150. func (opts FindRunnerOptions) toOrder() string {
  151. switch opts.Sort {
  152. case "online":
  153. return "last_online DESC"
  154. case "offline":
  155. return "last_online ASC"
  156. case "alphabetically":
  157. return "name ASC"
  158. }
  159. return "last_online DESC"
  160. }
  161. func CountRunners(ctx context.Context, opts FindRunnerOptions) (int64, error) {
  162. return db.GetEngine(ctx).
  163. Where(opts.toCond()).
  164. Count(ActionRunner{})
  165. }
  166. func FindRunners(ctx context.Context, opts FindRunnerOptions) (runners RunnerList, err error) {
  167. sess := db.GetEngine(ctx).
  168. Where(opts.toCond()).
  169. OrderBy(opts.toOrder())
  170. if opts.Page > 0 {
  171. sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
  172. }
  173. return runners, sess.Find(&runners)
  174. }
  175. // GetRunnerByUUID returns a runner via uuid
  176. func GetRunnerByUUID(ctx context.Context, uuid string) (*ActionRunner, error) {
  177. var runner ActionRunner
  178. has, err := db.GetEngine(ctx).Where("uuid=?", uuid).Get(&runner)
  179. if err != nil {
  180. return nil, err
  181. } else if !has {
  182. return nil, fmt.Errorf("runner with uuid %s: %w", uuid, util.ErrNotExist)
  183. }
  184. return &runner, nil
  185. }
  186. // GetRunnerByID returns a runner via id
  187. func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) {
  188. var runner ActionRunner
  189. has, err := db.GetEngine(ctx).Where("id=?", id).Get(&runner)
  190. if err != nil {
  191. return nil, err
  192. } else if !has {
  193. return nil, fmt.Errorf("runner with id %d: %w", id, util.ErrNotExist)
  194. }
  195. return &runner, nil
  196. }
  197. // UpdateRunner updates runner's information.
  198. func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
  199. e := db.GetEngine(ctx)
  200. var err error
  201. if len(cols) == 0 {
  202. _, err = e.ID(r.ID).AllCols().Update(r)
  203. } else {
  204. _, err = e.ID(r.ID).Cols(cols...).Update(r)
  205. }
  206. return err
  207. }
  208. // DeleteRunner deletes a runner by given ID.
  209. func DeleteRunner(ctx context.Context, id int64) error {
  210. if _, err := GetRunnerByID(ctx, id); err != nil {
  211. return err
  212. }
  213. _, err := db.GetEngine(ctx).Delete(&ActionRunner{ID: id})
  214. return err
  215. }
  216. // CreateRunner creates new runner.
  217. func CreateRunner(ctx context.Context, t *ActionRunner) error {
  218. _, err := db.GetEngine(ctx).Insert(t)
  219. return err
  220. }