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.

migrate.go 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2019 Gitea. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package task
  5. import (
  6. "context"
  7. "errors"
  8. "fmt"
  9. "strings"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/graceful"
  12. "code.gitea.io/gitea/modules/json"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/migrations"
  15. migration "code.gitea.io/gitea/modules/migrations/base"
  16. "code.gitea.io/gitea/modules/notification"
  17. "code.gitea.io/gitea/modules/process"
  18. "code.gitea.io/gitea/modules/structs"
  19. "code.gitea.io/gitea/modules/timeutil"
  20. "code.gitea.io/gitea/modules/util"
  21. )
  22. func handleCreateError(owner *models.User, err error) error {
  23. switch {
  24. case models.IsErrReachLimitOfRepo(err):
  25. return fmt.Errorf("You have already reached your limit of %d repositories", owner.MaxCreationLimit())
  26. case models.IsErrRepoAlreadyExist(err):
  27. return errors.New("The repository name is already used")
  28. case models.IsErrNameReserved(err):
  29. return fmt.Errorf("The repository name '%s' is reserved", err.(models.ErrNameReserved).Name)
  30. case models.IsErrNamePatternNotAllowed(err):
  31. return fmt.Errorf("The pattern '%s' is not allowed in a repository name", err.(models.ErrNamePatternNotAllowed).Pattern)
  32. default:
  33. return err
  34. }
  35. }
  36. func runMigrateTask(t *models.Task) (err error) {
  37. defer func() {
  38. if e := recover(); e != nil {
  39. err = fmt.Errorf("PANIC whilst trying to do migrate task: %v", e)
  40. log.Critical("PANIC during runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d]: %v\nStacktrace: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, e, log.Stack(2))
  41. }
  42. if err == nil {
  43. err = models.FinishMigrateTask(t)
  44. if err == nil {
  45. notification.NotifyMigrateRepository(t.Doer, t.Owner, t.Repo)
  46. return
  47. }
  48. log.Error("FinishMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, err)
  49. }
  50. t.EndTime = timeutil.TimeStampNow()
  51. t.Status = structs.TaskStatusFailed
  52. t.Message = err.Error()
  53. t.RepoID = 0
  54. if err := t.UpdateCols("status", "errors", "repo_id", "end_time"); err != nil {
  55. log.Error("Task UpdateCols failed: %v", err)
  56. }
  57. if t.Repo != nil {
  58. if errDelete := models.DeleteRepository(t.Doer, t.OwnerID, t.Repo.ID); errDelete != nil {
  59. log.Error("DeleteRepository: %v", errDelete)
  60. }
  61. }
  62. }()
  63. if err = t.LoadRepo(); err != nil {
  64. return
  65. }
  66. // if repository is ready, then just finish the task
  67. if t.Repo.Status == models.RepositoryReady {
  68. return nil
  69. }
  70. if err = t.LoadDoer(); err != nil {
  71. return
  72. }
  73. if err = t.LoadOwner(); err != nil {
  74. return
  75. }
  76. var opts *migration.MigrateOptions
  77. opts, err = t.MigrateConfig()
  78. if err != nil {
  79. return
  80. }
  81. opts.MigrateToRepoID = t.RepoID
  82. ctx, cancel := context.WithCancel(graceful.GetManager().ShutdownContext())
  83. defer cancel()
  84. pm := process.GetManager()
  85. pid := pm.Add(fmt.Sprintf("MigrateTask: %s/%s", t.Owner.Name, opts.RepoName), cancel)
  86. defer pm.Remove(pid)
  87. t.StartTime = timeutil.TimeStampNow()
  88. t.Status = structs.TaskStatusRunning
  89. if err = t.UpdateCols("start_time", "status"); err != nil {
  90. return
  91. }
  92. t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
  93. message := models.TranslatableMessage{
  94. Format: format,
  95. Args: args,
  96. }
  97. bs, _ := json.Marshal(message)
  98. t.Message = string(bs)
  99. _ = t.UpdateCols("message")
  100. })
  101. if err == nil {
  102. log.Trace("Repository migrated [%d]: %s/%s", t.Repo.ID, t.Owner.Name, t.Repo.Name)
  103. return
  104. }
  105. if models.IsErrRepoAlreadyExist(err) {
  106. err = errors.New("The repository name is already used")
  107. return
  108. }
  109. // remoteAddr may contain credentials, so we sanitize it
  110. err = util.NewStringURLSanitizedError(err, opts.CloneAddr, true)
  111. if strings.Contains(err.Error(), "Authentication failed") ||
  112. strings.Contains(err.Error(), "could not read Username") {
  113. return fmt.Errorf("Authentication failed: %v", err.Error())
  114. } else if strings.Contains(err.Error(), "fatal:") {
  115. return fmt.Errorf("Migration failed: %v", err.Error())
  116. }
  117. // do not be tempted to coalesce this line with the return
  118. err = handleCreateError(t.Owner, err)
  119. return
  120. }