2019-10-13 21:23:14 +08:00
// Copyright 2019 Gitea. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package task
import (
2020-12-03 02:36:06 +08:00
"context"
2019-10-13 21:23:14 +08:00
"errors"
"fmt"
"strings"
"code.gitea.io/gitea/models"
2019-12-17 12:16:54 +08:00
"code.gitea.io/gitea/modules/graceful"
2019-10-13 21:23:14 +08:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations"
2020-09-11 06:29:19 +08:00
migration "code.gitea.io/gitea/modules/migrations/base"
2019-10-13 21:23:14 +08:00
"code.gitea.io/gitea/modules/notification"
2020-12-03 02:36:06 +08:00
"code.gitea.io/gitea/modules/process"
2019-10-13 21:23:14 +08:00
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
2021-06-17 06:02:24 +08:00
jsoniter "github.com/json-iterator/go"
2019-10-13 21:23:14 +08:00
)
2020-10-24 07:46:35 +08:00
func handleCreateError ( owner * models . User , err error ) error {
2019-10-13 21:23:14 +08:00
switch {
case models . IsErrReachLimitOfRepo ( err ) :
return fmt . Errorf ( "You have already reached your limit of %d repositories" , owner . MaxCreationLimit ( ) )
case models . IsErrRepoAlreadyExist ( err ) :
return errors . New ( "The repository name is already used" )
case models . IsErrNameReserved ( err ) :
return fmt . Errorf ( "The repository name '%s' is reserved" , err . ( models . ErrNameReserved ) . Name )
case models . IsErrNamePatternNotAllowed ( err ) :
return fmt . Errorf ( "The pattern '%s' is not allowed in a repository name" , err . ( models . ErrNamePatternNotAllowed ) . Pattern )
default :
return err
}
}
func runMigrateTask ( t * models . Task ) ( err error ) {
defer func ( ) {
if e := recover ( ) ; e != nil {
2020-10-24 07:46:35 +08:00
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 21:23:14 +08:00
}
if err == nil {
err = models . FinishMigrateTask ( t )
if err == nil {
notification . NotifyMigrateRepository ( t . Doer , t . Owner , t . Repo )
return
}
2020-10-12 02:51:13 +08:00
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 21:23:14 +08:00
}
t . EndTime = timeutil . TimeStampNow ( )
t . Status = structs . TaskStatusFailed
2021-06-17 06:02:24 +08:00
t . Message = err . Error ( )
2020-10-24 07:46:35 +08:00
t . RepoID = 0
if err := t . UpdateCols ( "status" , "errors" , "repo_id" , "end_time" ) ; err != nil {
2020-10-12 02:51:13 +08:00
log . Error ( "Task UpdateCols failed: %v" , err )
2019-10-13 21:23:14 +08:00
}
if t . Repo != nil {
if errDelete := models . DeleteRepository ( t . Doer , t . OwnerID , t . Repo . ID ) ; errDelete != nil {
log . Error ( "DeleteRepository: %v" , errDelete )
}
}
} ( )
2020-10-24 07:46:35 +08:00
if err = t . LoadRepo ( ) ; err != nil {
return
2019-10-13 21:23:14 +08:00
}
// if repository is ready, then just finsih the task
if t . Repo . Status == models . RepositoryReady {
return nil
}
2020-10-24 07:46:35 +08:00
if err = t . LoadDoer ( ) ; err != nil {
return
2019-10-13 21:23:14 +08:00
}
2020-10-24 07:46:35 +08:00
if err = t . LoadOwner ( ) ; err != nil {
return
2019-10-13 21:23:14 +08:00
}
2020-09-11 06:29:19 +08:00
var opts * migration . MigrateOptions
2019-10-13 21:23:14 +08:00
opts , err = t . MigrateConfig ( )
if err != nil {
2020-10-24 07:46:35 +08:00
return
2019-10-13 21:23:14 +08:00
}
opts . MigrateToRepoID = t . RepoID
2020-10-24 07:46:35 +08:00
var repo * models . Repository
2020-12-03 02:36:06 +08:00
ctx , cancel := context . WithCancel ( graceful . GetManager ( ) . ShutdownContext ( ) )
defer cancel ( )
pm := process . GetManager ( )
pid := pm . Add ( fmt . Sprintf ( "MigrateTask: %s/%s" , t . Owner . Name , opts . RepoName ) , cancel )
defer pm . Remove ( pid )
t . StartTime = timeutil . TimeStampNow ( )
t . Status = structs . TaskStatusRunning
if err = t . UpdateCols ( "start_time" , "status" ) ; err != nil {
return
}
2021-06-17 06:02:24 +08:00
repo , err = migrations . MigrateRepository ( ctx , t . Doer , t . Owner . Name , * opts , func ( format string , args ... interface { } ) {
message := models . TranslatableMessage {
Format : format ,
Args : args ,
}
json := jsoniter . ConfigCompatibleWithStandardLibrary
bs , _ := json . Marshal ( message )
t . Message = string ( bs )
_ = t . UpdateCols ( "message" )
} )
2019-10-13 21:23:14 +08:00
if err == nil {
log . Trace ( "Repository migrated [%d]: %s/%s" , repo . ID , t . Owner . Name , repo . Name )
2020-10-24 07:46:35 +08:00
return
2019-10-13 21:23:14 +08:00
}
if models . IsErrRepoAlreadyExist ( err ) {
2020-10-24 07:46:35 +08:00
err = errors . New ( "The repository name is already used" )
return
2019-10-13 21:23:14 +08:00
}
// remoteAddr may contain credentials, so we sanitize it
2021-06-15 01:20:43 +08:00
err = util . NewStringURLSanitizedError ( err , opts . CloneAddr , true )
2019-10-13 21:23:14 +08:00
if strings . Contains ( err . Error ( ) , "Authentication failed" ) ||
strings . Contains ( err . Error ( ) , "could not read Username" ) {
return fmt . Errorf ( "Authentication failed: %v" , err . Error ( ) )
} else if strings . Contains ( err . Error ( ) , "fatal:" ) {
return fmt . Errorf ( "Migration failed: %v" , err . Error ( ) )
}
2020-10-24 07:46:35 +08:00
// do not be tempted to coalesce this line with the return
err = handleCreateError ( t . Owner , err )
return
2019-10-13 21:23:14 +08:00
}