code.gitea.io/gitea@v1.21.7/services/task/task.go (about) 1 // Copyright 2019 Gitea. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package task 5 6 import ( 7 "context" 8 "fmt" 9 10 admin_model "code.gitea.io/gitea/models/admin" 11 "code.gitea.io/gitea/models/db" 12 repo_model "code.gitea.io/gitea/models/repo" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/graceful" 15 "code.gitea.io/gitea/modules/json" 16 "code.gitea.io/gitea/modules/log" 17 base "code.gitea.io/gitea/modules/migration" 18 "code.gitea.io/gitea/modules/queue" 19 "code.gitea.io/gitea/modules/secret" 20 "code.gitea.io/gitea/modules/setting" 21 "code.gitea.io/gitea/modules/structs" 22 "code.gitea.io/gitea/modules/timeutil" 23 "code.gitea.io/gitea/modules/util" 24 repo_service "code.gitea.io/gitea/services/repository" 25 ) 26 27 // taskQueue is a global queue of tasks 28 var taskQueue *queue.WorkerPoolQueue[*admin_model.Task] 29 30 // Run a task 31 func Run(ctx context.Context, t *admin_model.Task) error { 32 switch t.Type { 33 case structs.TaskTypeMigrateRepo: 34 return runMigrateTask(ctx, t) 35 default: 36 return fmt.Errorf("Unknown task type: %d", t.Type) 37 } 38 } 39 40 // Init will start the service to get all unfinished tasks and run them 41 func Init() error { 42 taskQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "task", handler) 43 if taskQueue == nil { 44 return fmt.Errorf("unable to create task queue") 45 } 46 go graceful.GetManager().RunWithCancel(taskQueue) 47 return nil 48 } 49 50 func handler(items ...*admin_model.Task) []*admin_model.Task { 51 for _, task := range items { 52 if err := Run(db.DefaultContext, task); err != nil { 53 log.Error("Run task failed: %v", err) 54 } 55 } 56 return nil 57 } 58 59 // MigrateRepository add migration repository to task 60 func MigrateRepository(ctx context.Context, doer, u *user_model.User, opts base.MigrateOptions) error { 61 task, err := CreateMigrateTask(ctx, doer, u, opts) 62 if err != nil { 63 return err 64 } 65 66 return taskQueue.Push(task) 67 } 68 69 // CreateMigrateTask creates a migrate task 70 func CreateMigrateTask(ctx context.Context, doer, u *user_model.User, opts base.MigrateOptions) (*admin_model.Task, error) { 71 // encrypt credentials for persistence 72 var err error 73 opts.CloneAddrEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.CloneAddr) 74 if err != nil { 75 return nil, err 76 } 77 opts.CloneAddr = util.SanitizeCredentialURLs(opts.CloneAddr) 78 opts.AuthPasswordEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.AuthPassword) 79 if err != nil { 80 return nil, err 81 } 82 opts.AuthPassword = "" 83 opts.AuthTokenEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.AuthToken) 84 if err != nil { 85 return nil, err 86 } 87 opts.AuthToken = "" 88 bs, err := json.Marshal(&opts) 89 if err != nil { 90 return nil, err 91 } 92 93 task := &admin_model.Task{ 94 DoerID: doer.ID, 95 OwnerID: u.ID, 96 Type: structs.TaskTypeMigrateRepo, 97 Status: structs.TaskStatusQueued, 98 PayloadContent: string(bs), 99 } 100 101 if err := admin_model.CreateTask(ctx, task); err != nil { 102 return nil, err 103 } 104 105 repo, err := repo_service.CreateRepositoryDirectly(ctx, doer, u, repo_service.CreateRepoOptions{ 106 Name: opts.RepoName, 107 Description: opts.Description, 108 OriginalURL: opts.OriginalURL, 109 GitServiceType: opts.GitServiceType, 110 IsPrivate: opts.Private, 111 IsMirror: opts.Mirror, 112 Status: repo_model.RepositoryBeingMigrated, 113 }) 114 if err != nil { 115 task.EndTime = timeutil.TimeStampNow() 116 task.Status = structs.TaskStatusFailed 117 err2 := task.UpdateCols(ctx, "end_time", "status") 118 if err2 != nil { 119 log.Error("UpdateCols Failed: %v", err2.Error()) 120 } 121 return nil, err 122 } 123 124 task.RepoID = repo.ID 125 if err = task.UpdateCols(ctx, "repo_id"); err != nil { 126 return nil, err 127 } 128 129 return task, nil 130 } 131 132 // RetryMigrateTask retry a migrate task 133 func RetryMigrateTask(ctx context.Context, repoID int64) error { 134 migratingTask, err := admin_model.GetMigratingTask(ctx, repoID) 135 if err != nil { 136 log.Error("GetMigratingTask: %v", err) 137 return err 138 } 139 if migratingTask.Status == structs.TaskStatusQueued || migratingTask.Status == structs.TaskStatusRunning { 140 return nil 141 } 142 143 // TODO Need to removing the storage/database garbage brought by the failed task 144 145 // Reset task status and messages 146 migratingTask.Status = structs.TaskStatusQueued 147 migratingTask.Message = "" 148 if err = migratingTask.UpdateCols(ctx, "status", "message"); err != nil { 149 log.Error("task.UpdateCols failed: %v", err) 150 return err 151 } 152 153 return taskQueue.Push(migratingTask) 154 }