1Panel/agent/app/task/task.go

244 lines
6.3 KiB
Go
Raw Normal View History

2024-07-23 14:48:37 +08:00
package task
import (
"context"
"fmt"
2024-07-29 18:09:57 +08:00
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/google/uuid"
2024-07-23 14:48:37 +08:00
"log"
"os"
"path"
"strconv"
"time"
)
2024-07-29 18:09:57 +08:00
type ActionFunc func(*Task) error
2024-08-02 16:00:15 +08:00
type RollbackFunc func(*Task)
2024-07-23 14:48:37 +08:00
type Task struct {
Name string
2024-07-29 18:09:57 +08:00
TaskID string
2024-07-23 14:48:37 +08:00
Logger *log.Logger
SubTasks []*SubTask
Rollbacks []RollbackFunc
logFile *os.File
2024-07-29 18:09:57 +08:00
taskRepo repo.ITaskRepo
Task *model.Task
ParentID string
2024-07-23 14:48:37 +08:00
}
type SubTask struct {
RootTask *Task
Name string
Retry int
Timeout time.Duration
Action ActionFunc
Rollback RollbackFunc
Error error
IgnoreErr bool
2024-07-23 14:48:37 +08:00
}
2024-07-29 18:09:57 +08:00
const (
TaskInstall = "TaskInstall"
TaskUninstall = "TaskUninstall"
TaskCreate = "TaskCreate"
TaskDelete = "TaskDelete"
TaskUpgrade = "TaskUpgrade"
TaskUpdate = "TaskUpdate"
TaskRestart = "TaskRestart"
2024-08-02 16:00:15 +08:00
TaskBackup = "TaskBackup"
2024-07-29 18:09:57 +08:00
)
const (
TaskScopeWebsite = "Website"
TaskScopeApp = "App"
TaskScopeRuntime = "Runtime"
TaskScopeDatabase = "Database"
)
const (
TaskSuccess = "Success"
TaskFailed = "Failed"
)
func GetTaskName(resourceName, operate, scope string) string {
return fmt.Sprintf("%s%s [%s]", i18n.GetMsgByKey(operate), i18n.GetMsgByKey(scope), resourceName)
}
func NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {
2024-08-02 16:00:15 +08:00
return NewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)
2024-07-29 18:09:57 +08:00
}
2024-08-02 16:00:15 +08:00
func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {
2024-07-29 18:09:57 +08:00
if taskID == "" {
taskID = uuid.New().String()
}
2024-08-02 16:00:15 +08:00
logDir := path.Join(constant.LogDir, taskScope)
2024-07-29 18:09:57 +08:00
if _, err := os.Stat(logDir); os.IsNotExist(err) {
if err = os.MkdirAll(logDir, 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
}
2024-08-02 16:00:15 +08:00
logPath := path.Join(constant.LogDir, taskScope, taskID+".log")
2024-07-23 14:48:37 +08:00
file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
logger := log.New(file, "", log.LstdFlags)
2024-07-29 18:09:57 +08:00
taskModel := &model.Task{
ID: taskID,
Name: name,
2024-08-02 16:00:15 +08:00
Type: taskScope,
LogFile: logPath,
Status: constant.StatusRunning,
ResourceID: resourceID,
2024-08-02 16:00:15 +08:00
Operate: operate,
2024-07-29 18:09:57 +08:00
}
taskRepo := repo.NewITaskRepo()
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel}
return task, nil
2024-07-23 14:48:37 +08:00
}
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
2024-07-29 18:09:57 +08:00
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}
2024-07-23 14:48:37 +08:00
t.SubTasks = append(t.SubTasks, subTask)
}
func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
2024-07-29 18:09:57 +08:00
subTask := &SubTask{RootTask: t, Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
2024-07-23 14:48:37 +08:00
t.SubTasks = append(t.SubTasks, subTask)
}
func (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) {
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: nil, IgnoreErr: true}
t.SubTasks = append(t.SubTasks, subTask)
}
2024-07-29 18:09:57 +08:00
func (s *SubTask) Execute() error {
s.RootTask.Log(s.Name)
var err error
2024-07-23 14:48:37 +08:00
for i := 0; i < s.Retry+1; i++ {
if i > 0 {
2024-07-29 18:09:57 +08:00
s.RootTask.Log(i18n.GetWithName("TaskRetry", strconv.Itoa(i)))
2024-07-23 14:48:37 +08:00
}
ctx, cancel := context.WithTimeout(context.Background(), s.Timeout)
defer cancel()
done := make(chan error)
go func() {
2024-07-29 18:09:57 +08:00
done <- s.Action(s.RootTask)
2024-07-23 14:48:37 +08:00
}()
select {
case <-ctx.Done():
2024-07-29 18:09:57 +08:00
s.RootTask.Log(i18n.GetWithName("TaskTimeout", s.Name))
case err = <-done:
2024-07-23 14:48:37 +08:00
if err != nil {
2024-07-29 18:09:57 +08:00
s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", s.Name, err))
2024-07-23 14:48:37 +08:00
} else {
2024-07-29 18:09:57 +08:00
s.RootTask.Log(i18n.GetWithName("SubTaskSuccess", s.Name))
return nil
2024-07-23 14:48:37 +08:00
}
}
if i == s.Retry {
if s.Rollback != nil {
2024-08-02 16:00:15 +08:00
s.Rollback(s.RootTask)
2024-07-23 14:48:37 +08:00
}
}
time.Sleep(1 * time.Second)
}
2024-07-29 18:09:57 +08:00
return err
}
func (t *Task) updateTask(task *model.Task) {
_ = t.taskRepo.Update(context.Background(), task)
2024-07-23 14:48:37 +08:00
}
func (t *Task) Execute() error {
2024-07-29 18:09:57 +08:00
if err := t.taskRepo.Create(context.Background(), t.Task); err != nil {
return err
}
2024-07-23 14:48:37 +08:00
var err error
2024-07-29 18:09:57 +08:00
t.Log(i18n.GetWithName("TaskStart", t.Name))
2024-07-23 14:48:37 +08:00
for _, subTask := range t.SubTasks {
2024-07-29 18:09:57 +08:00
t.Task.CurrentStep = subTask.Name
t.updateTask(t.Task)
if err = subTask.Execute(); err == nil {
2024-07-23 14:48:37 +08:00
if subTask.Rollback != nil {
t.Rollbacks = append(t.Rollbacks, subTask.Rollback)
}
} else {
if subTask.IgnoreErr {
err = nil
continue
}
2024-07-29 18:09:57 +08:00
t.Task.ErrorMsg = err.Error()
t.Task.Status = constant.StatusFailed
2024-07-23 14:48:37 +08:00
for _, rollback := range t.Rollbacks {
2024-08-02 16:00:15 +08:00
rollback(t)
2024-07-23 14:48:37 +08:00
}
2024-07-29 18:09:57 +08:00
t.updateTask(t.Task)
2024-07-23 14:48:37 +08:00
break
}
}
2024-07-29 18:09:57 +08:00
if t.Task.Status == constant.Running {
t.Task.Status = constant.StatusSuccess
t.Log(i18n.GetWithName("TaskSuccess", t.Name))
} else {
t.Log(i18n.GetWithName("TaskFailed", t.Name))
}
t.Log("[TASK-END]")
t.Task.EndAt = time.Now()
t.updateTask(t.Task)
2024-07-23 14:48:37 +08:00
_ = t.logFile.Close()
return err
}
2024-07-29 18:09:57 +08:00
func (t *Task) DeleteLogFile() {
_ = os.Remove(t.Task.LogFile)
}
func (t *Task) LogWithStatus(msg string, err error) {
if err != nil {
t.Logger.Printf(i18n.GetWithNameAndErr("FailedStatus", msg, err))
} else {
t.Logger.Printf(i18n.GetWithName("SuccessStatus", msg))
}
}
func (t *Task) Log(msg string) {
t.Logger.Printf(msg)
}
func (t *Task) LogFailed(msg string) {
t.Logger.Printf(msg + i18n.GetMsgByKey("Failed"))
}
func (t *Task) LogFailedWithErr(msg string, err error) {
t.Logger.Printf(fmt.Sprintf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error()))
}
func (t *Task) LogSuccess(msg string) {
t.Logger.Printf(msg + i18n.GetMsgByKey("Success"))
}
func (t *Task) LogStart(msg string) {
t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg))
}
func (t *Task) LogWithOps(operate, msg string) {
t.Logger.Printf("%s%s", i18n.GetMsgByKey(operate), msg)
}
func (t *Task) LogSuccessWithOps(operate, msg string) {
t.Logger.Printf("%s%s%s", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Success"))
}
func (t *Task) LogFailedWithOps(operate, msg string, err error) {
t.Logger.Printf("%s%s%s : %s ", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Failed"), err.Error())
}