2019-10-13 18:53:14 +05:30
// Copyright 2019 Gitea. All rights reserved.
2022-11-27 23:50:29 +05:30
// SPDX-License-Identifier: MIT
2019-10-13 18:53:14 +05:30
package task
import (
"errors"
"fmt"
"strings"
2023-05-11 13:55:46 +05:30
"time"
2019-10-13 18:53:14 +05:30
2022-08-25 08:01:57 +05:30
admin_model "code.gitea.io/gitea/models/admin"
2021-11-24 15:19:20 +05:30
"code.gitea.io/gitea/models/db"
2021-12-10 06:57:50 +05:30
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 15:19:20 +05:30
user_model "code.gitea.io/gitea/models/user"
2019-12-17 09:46:54 +05:30
"code.gitea.io/gitea/modules/graceful"
2021-07-24 21:33:58 +05:30
"code.gitea.io/gitea/modules/json"
2019-10-13 18:53:14 +05:30
"code.gitea.io/gitea/modules/log"
2021-11-16 20:55:33 +05:30
"code.gitea.io/gitea/modules/migration"
2019-10-13 18:53:14 +05:30
"code.gitea.io/gitea/modules/notification"
2020-12-03 00:06:06 +05:30
"code.gitea.io/gitea/modules/process"
2019-10-13 18:53:14 +05:30
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
2021-11-16 20:55:33 +05:30
"code.gitea.io/gitea/services/migrations"
2019-10-13 18:53:14 +05:30
)
2021-11-24 15:19:20 +05:30
func handleCreateError ( owner * user_model . User , err error ) error {
2019-10-13 18:53:14 +05:30
switch {
2021-12-12 21:18:20 +05:30
case repo_model . IsErrReachLimitOfRepo ( err ) :
2023-05-11 13:55:46 +05:30
return fmt . Errorf ( "you have already reached your limit of %d repositories" , owner . MaxCreationLimit ( ) )
2021-12-12 21:18:20 +05:30
case repo_model . IsErrRepoAlreadyExist ( err ) :
2023-05-11 13:55:46 +05:30
return errors . New ( "the repository name is already used" )
2021-11-24 15:19:20 +05:30
case db . IsErrNameReserved ( err ) :
2023-05-11 13:55:46 +05:30
return fmt . Errorf ( "the repository name '%s' is reserved" , err . ( db . ErrNameReserved ) . Name )
2021-11-24 15:19:20 +05:30
case db . IsErrNamePatternNotAllowed ( err ) :
2023-05-11 13:55:46 +05:30
return fmt . Errorf ( "the pattern '%s' is not allowed in a repository name" , err . ( db . ErrNamePatternNotAllowed ) . Pattern )
2019-10-13 18:53:14 +05:30
default :
return err
}
}
2022-08-25 08:01:57 +05:30
func runMigrateTask ( t * admin_model . Task ) ( err error ) {
2019-10-13 18:53:14 +05:30
defer func ( ) {
if e := recover ( ) ; e != nil {
2020-10-24 05:16:35 +05:30
err = fmt . Errorf ( "PANIC whilst trying to do migrate task: %v" , e )
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 ) )
2019-10-13 18:53:14 +05:30
}
if err == nil {
2022-08-25 08:01:57 +05:30
err = admin_model . FinishMigrateTask ( t )
2019-10-13 18:53:14 +05:30
if err == nil {
2022-11-19 13:42:33 +05:30
notification . NotifyMigrateRepository ( db . DefaultContext , t . Doer , t . Owner , t . Repo )
2019-10-13 18:53:14 +05:30
return
}
2020-10-12 00:21:13 +05:30
log . Error ( "FinishMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v" , t . ID , t . DoerID , t . RepoID , t . OwnerID , err )
2019-10-13 18:53:14 +05:30
}
2023-05-11 13:55:46 +05:30
log . Error ( "runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v" , t . ID , t . DoerID , t . RepoID , t . OwnerID , err )
2019-10-13 18:53:14 +05:30
t . EndTime = timeutil . TimeStampNow ( )
t . Status = structs . TaskStatusFailed
2021-06-17 03:32:24 +05:30
t . Message = err . Error ( )
2021-11-13 16:58:50 +05:30
2023-05-11 13:55:46 +05:30
if err := t . UpdateCols ( "status" , "message" , "end_time" ) ; err != nil {
2020-10-12 00:21:13 +05:30
log . Error ( "Task UpdateCols failed: %v" , err )
2019-10-13 18:53:14 +05:30
}
2023-05-11 13:55:46 +05:30
// then, do not delete the repository, otherwise the users won't be able to see the last error
2019-10-13 18:53:14 +05:30
} ( )
2020-10-24 05:16:35 +05:30
if err = t . LoadRepo ( ) ; err != nil {
2023-07-09 17:28:06 +05:30
return err
2019-10-13 18:53:14 +05:30
}
2021-07-08 17:08:13 +05:30
// if repository is ready, then just finish the task
2021-12-10 06:57:50 +05:30
if t . Repo . Status == repo_model . RepositoryReady {
2019-10-13 18:53:14 +05:30
return nil
}
2020-10-24 05:16:35 +05:30
if err = t . LoadDoer ( ) ; err != nil {
2023-07-09 17:28:06 +05:30
return err
2019-10-13 18:53:14 +05:30
}
2020-10-24 05:16:35 +05:30
if err = t . LoadOwner ( ) ; err != nil {
2023-07-09 17:28:06 +05:30
return err
2019-10-13 18:53:14 +05:30
}
2020-09-11 03:59:19 +05:30
var opts * migration . MigrateOptions
2019-10-13 18:53:14 +05:30
opts , err = t . MigrateConfig ( )
if err != nil {
2023-07-09 17:28:06 +05:30
return err
2019-10-13 18:53:14 +05:30
}
opts . MigrateToRepoID = t . RepoID
2020-12-03 00:06:06 +05:30
pm := process . GetManager ( )
2023-05-11 13:55:46 +05:30
ctx , cancel , finished := pm . AddContext ( graceful . GetManager ( ) . ShutdownContext ( ) , fmt . Sprintf ( "MigrateTask: %s/%s" , t . Owner . Name , opts . RepoName ) )
2021-12-01 01:36:32 +05:30
defer finished ( )
2020-12-03 00:06:06 +05:30
t . StartTime = timeutil . TimeStampNow ( )
t . Status = structs . TaskStatusRunning
if err = t . UpdateCols ( "start_time" , "status" ) ; err != nil {
2023-07-09 17:28:06 +05:30
return err
2020-12-03 00:06:06 +05:30
}
2023-05-11 13:55:46 +05:30
// check whether the task should be canceled, this goroutine is also managed by process manager
go func ( ) {
for {
select {
case <- time . After ( 2 * time . Second ) :
case <- ctx . Done ( ) :
return
}
task , _ := admin_model . GetMigratingTask ( t . RepoID )
if task != nil && task . Status != structs . TaskStatusRunning {
log . Debug ( "MigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] is canceled due to status is not 'running'" , t . ID , t . DoerID , t . RepoID , t . OwnerID )
cancel ( )
return
}
}
} ( )
2023-07-05 00:06:08 +05:30
t . Repo , err = migrations . MigrateRepository ( ctx , t . Doer , t . Owner . Name , * opts , func ( format string , args ... any ) {
2022-08-25 08:01:57 +05:30
message := admin_model . TranslatableMessage {
2021-06-17 03:32:24 +05:30
Format : format ,
Args : args ,
}
bs , _ := json . Marshal ( message )
t . Message = string ( bs )
_ = t . UpdateCols ( "message" )
} )
2023-05-11 13:55:46 +05:30
2019-10-13 18:53:14 +05:30
if err == nil {
2021-09-08 23:13:19 +05:30
log . Trace ( "Repository migrated [%d]: %s/%s" , t . Repo . ID , t . Owner . Name , t . Repo . Name )
2023-07-09 17:28:06 +05:30
return nil
2019-10-13 18:53:14 +05:30
}
2021-12-12 21:18:20 +05:30
if repo_model . IsErrRepoAlreadyExist ( err ) {
2023-07-09 17:28:06 +05:30
return errors . New ( "the repository name is already used" )
2019-10-13 18:53:14 +05:30
}
// remoteAddr may contain credentials, so we sanitize it
2022-03-31 07:55:40 +05:30
err = util . SanitizeErrorCredentialURLs ( err )
2019-10-13 18:53:14 +05:30
if strings . Contains ( err . Error ( ) , "Authentication failed" ) ||
strings . Contains ( err . Error ( ) , "could not read Username" ) {
2023-05-11 13:55:46 +05:30
return fmt . Errorf ( "authentication failed: %w" , err )
2019-10-13 18:53:14 +05:30
} else if strings . Contains ( err . Error ( ) , "fatal:" ) {
2023-05-11 13:55:46 +05:30
return fmt . Errorf ( "migration failed: %w" , err )
2019-10-13 18:53:14 +05:30
}
2020-10-24 05:16:35 +05:30
// do not be tempted to coalesce this line with the return
err = handleCreateError ( t . Owner , err )
2022-06-20 15:32:49 +05:30
return err
2019-10-13 18:53:14 +05:30
}