9302eba971
* DBContext is just a Context This PR removes some of the specialness from the DBContext and makes it context This allows us to simplify the GetEngine code to wrap around any context in future and means that we can change our loadRepo(e Engine) functions to simply take contexts. Signed-off-by: Andrew Thornton <art27@cantab.net> * fix unit tests Signed-off-by: Andrew Thornton <art27@cantab.net> * another place that needs to set the initial context Signed-off-by: Andrew Thornton <art27@cantab.net> * avoid race Signed-off-by: Andrew Thornton <art27@cantab.net> * change attachment error Signed-off-by: Andrew Thornton <art27@cantab.net>
266 lines
6.7 KiB
Go
266 lines
6.7 KiB
Go
// 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 models
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/modules/json"
|
|
migration "code.gitea.io/gitea/modules/migrations/base"
|
|
"code.gitea.io/gitea/modules/secret"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
// Task represents a task
|
|
type Task struct {
|
|
ID int64
|
|
DoerID int64 `xorm:"index"` // operator
|
|
Doer *User `xorm:"-"`
|
|
OwnerID int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero
|
|
Owner *User `xorm:"-"`
|
|
RepoID int64 `xorm:"index"`
|
|
Repo *Repository `xorm:"-"`
|
|
Type structs.TaskType
|
|
Status structs.TaskStatus `xorm:"index"`
|
|
StartTime timeutil.TimeStamp
|
|
EndTime timeutil.TimeStamp
|
|
PayloadContent string `xorm:"TEXT"`
|
|
Message string `xorm:"TEXT"` // if task failed, saved the error reason
|
|
Created timeutil.TimeStamp `xorm:"created"`
|
|
}
|
|
|
|
func init() {
|
|
db.RegisterModel(new(Task))
|
|
}
|
|
|
|
// TranslatableMessage represents JSON struct that can be translated with a Locale
|
|
type TranslatableMessage struct {
|
|
Format string
|
|
Args []interface{} `json:"omitempty"`
|
|
}
|
|
|
|
// LoadRepo loads repository of the task
|
|
func (task *Task) LoadRepo() error {
|
|
return task.loadRepo(db.GetEngine(db.DefaultContext))
|
|
}
|
|
|
|
func (task *Task) loadRepo(e db.Engine) error {
|
|
if task.Repo != nil {
|
|
return nil
|
|
}
|
|
var repo Repository
|
|
has, err := e.ID(task.RepoID).Get(&repo)
|
|
if err != nil {
|
|
return err
|
|
} else if !has {
|
|
return ErrRepoNotExist{
|
|
ID: task.RepoID,
|
|
}
|
|
}
|
|
task.Repo = &repo
|
|
return nil
|
|
}
|
|
|
|
// LoadDoer loads do user
|
|
func (task *Task) LoadDoer() error {
|
|
if task.Doer != nil {
|
|
return nil
|
|
}
|
|
|
|
var doer User
|
|
has, err := db.GetEngine(db.DefaultContext).ID(task.DoerID).Get(&doer)
|
|
if err != nil {
|
|
return err
|
|
} else if !has {
|
|
return ErrUserNotExist{
|
|
UID: task.DoerID,
|
|
}
|
|
}
|
|
task.Doer = &doer
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadOwner loads owner user
|
|
func (task *Task) LoadOwner() error {
|
|
if task.Owner != nil {
|
|
return nil
|
|
}
|
|
|
|
var owner User
|
|
has, err := db.GetEngine(db.DefaultContext).ID(task.OwnerID).Get(&owner)
|
|
if err != nil {
|
|
return err
|
|
} else if !has {
|
|
return ErrUserNotExist{
|
|
UID: task.OwnerID,
|
|
}
|
|
}
|
|
task.Owner = &owner
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateCols updates some columns
|
|
func (task *Task) UpdateCols(cols ...string) error {
|
|
_, err := db.GetEngine(db.DefaultContext).ID(task.ID).Cols(cols...).Update(task)
|
|
return err
|
|
}
|
|
|
|
// MigrateConfig returns task config when migrate repository
|
|
func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
|
|
if task.Type == structs.TaskTypeMigrateRepo {
|
|
var opts migration.MigrateOptions
|
|
err := json.Unmarshal([]byte(task.PayloadContent), &opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// decrypt credentials
|
|
if opts.CloneAddrEncrypted != "" {
|
|
if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if opts.AuthPasswordEncrypted != "" {
|
|
if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if opts.AuthTokenEncrypted != "" {
|
|
if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &opts, nil
|
|
}
|
|
return nil, fmt.Errorf("Task type is %s, not Migrate Repo", task.Type.Name())
|
|
}
|
|
|
|
// ErrTaskDoesNotExist represents a "TaskDoesNotExist" kind of error.
|
|
type ErrTaskDoesNotExist struct {
|
|
ID int64
|
|
RepoID int64
|
|
Type structs.TaskType
|
|
}
|
|
|
|
// IsErrTaskDoesNotExist checks if an error is a ErrTaskIsNotExist.
|
|
func IsErrTaskDoesNotExist(err error) bool {
|
|
_, ok := err.(ErrTaskDoesNotExist)
|
|
return ok
|
|
}
|
|
|
|
func (err ErrTaskDoesNotExist) Error() string {
|
|
return fmt.Sprintf("task is not exist [id: %d, repo_id: %d, type: %d]",
|
|
err.ID, err.RepoID, err.Type)
|
|
}
|
|
|
|
// GetMigratingTask returns the migrating task by repo's id
|
|
func GetMigratingTask(repoID int64) (*Task, error) {
|
|
task := Task{
|
|
RepoID: repoID,
|
|
Type: structs.TaskTypeMigrateRepo,
|
|
}
|
|
has, err := db.GetEngine(db.DefaultContext).Get(&task)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !has {
|
|
return nil, ErrTaskDoesNotExist{0, repoID, task.Type}
|
|
}
|
|
return &task, nil
|
|
}
|
|
|
|
// GetMigratingTaskByID returns the migrating task by repo's id
|
|
func GetMigratingTaskByID(id, doerID int64) (*Task, *migration.MigrateOptions, error) {
|
|
task := Task{
|
|
ID: id,
|
|
DoerID: doerID,
|
|
Type: structs.TaskTypeMigrateRepo,
|
|
}
|
|
has, err := db.GetEngine(db.DefaultContext).Get(&task)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
} else if !has {
|
|
return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type}
|
|
}
|
|
|
|
var opts migration.MigrateOptions
|
|
if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return &task, &opts, nil
|
|
}
|
|
|
|
// FindTaskOptions find all tasks
|
|
type FindTaskOptions struct {
|
|
Status int
|
|
}
|
|
|
|
// ToConds generates conditions for database operation.
|
|
func (opts FindTaskOptions) ToConds() builder.Cond {
|
|
cond := builder.NewCond()
|
|
if opts.Status >= 0 {
|
|
cond = cond.And(builder.Eq{"status": opts.Status})
|
|
}
|
|
return cond
|
|
}
|
|
|
|
// FindTasks find all tasks
|
|
func FindTasks(opts FindTaskOptions) ([]*Task, error) {
|
|
tasks := make([]*Task, 0, 10)
|
|
err := db.GetEngine(db.DefaultContext).Where(opts.ToConds()).Find(&tasks)
|
|
return tasks, err
|
|
}
|
|
|
|
// CreateTask creates a task on database
|
|
func CreateTask(task *Task) error {
|
|
return createTask(db.GetEngine(db.DefaultContext), task)
|
|
}
|
|
|
|
func createTask(e db.Engine, task *Task) error {
|
|
_, err := e.Insert(task)
|
|
return err
|
|
}
|
|
|
|
// FinishMigrateTask updates database when migrate task finished
|
|
func FinishMigrateTask(task *Task) error {
|
|
task.Status = structs.TaskStatusFinished
|
|
task.EndTime = timeutil.TimeStampNow()
|
|
|
|
// delete credentials when we're done, they're a liability.
|
|
conf, err := task.MigrateConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conf.AuthPassword = ""
|
|
conf.AuthToken = ""
|
|
conf.CloneAddr = util.NewStringURLSanitizer(conf.CloneAddr, true).Replace(conf.CloneAddr)
|
|
conf.AuthPasswordEncrypted = ""
|
|
conf.AuthTokenEncrypted = ""
|
|
conf.CloneAddrEncrypted = ""
|
|
confBytes, err := json.Marshal(conf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.PayloadContent = string(confBytes)
|
|
|
|
sess := db.NewSession(db.DefaultContext)
|
|
defer sess.Close()
|
|
if err := sess.Begin(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := sess.ID(task.ID).Cols("status", "end_time", "payload_content").Update(task); err != nil {
|
|
return err
|
|
}
|
|
|
|
return sess.Commit()
|
|
}
|