mirror of
https://github.com/garethgeorge/backrest.git
synced 2025-12-10 15:55:41 +00:00
152 lines
3.6 KiB
Go
152 lines
3.6 KiB
Go
package tasks
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
v1 "github.com/garethgeorge/backrest/gen/go/v1"
|
|
"github.com/garethgeorge/backrest/internal/oplog"
|
|
"github.com/garethgeorge/backrest/internal/protoutil"
|
|
)
|
|
|
|
type CheckTask struct {
|
|
BaseTask
|
|
force bool
|
|
didRun bool
|
|
}
|
|
|
|
func NewCheckTask(repoID, planID string, force bool) Task {
|
|
return &CheckTask{
|
|
BaseTask: BaseTask{
|
|
TaskType: "check",
|
|
TaskName: fmt.Sprintf("check for repo %q", repoID),
|
|
TaskRepoID: repoID,
|
|
TaskPlanID: planID,
|
|
},
|
|
force: force,
|
|
}
|
|
}
|
|
|
|
func (t *CheckTask) Next(now time.Time, runner TaskRunner) (ScheduledTask, error) {
|
|
if t.force {
|
|
if t.didRun {
|
|
return NeverScheduledTask, nil
|
|
}
|
|
t.didRun = true
|
|
return ScheduledTask{
|
|
Task: t,
|
|
RunAt: now,
|
|
Op: &v1.Operation{
|
|
Op: &v1.Operation_OperationCheck{},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
repo, err := runner.GetRepo(t.RepoID())
|
|
if err != nil {
|
|
return ScheduledTask{}, fmt.Errorf("get repo %v: %w", t.RepoID(), err)
|
|
}
|
|
|
|
if repo.CheckPolicy.GetSchedule() == nil {
|
|
return NeverScheduledTask, nil
|
|
}
|
|
|
|
var lastRan time.Time
|
|
var foundBackup bool
|
|
|
|
if err := runner.OpLog().Query(oplog.Query{RepoID: t.RepoID(), Reversed: true}, func(op *v1.Operation) error {
|
|
if op.Status == v1.OperationStatus_STATUS_PENDING || op.Status == v1.OperationStatus_STATUS_SYSTEM_CANCELLED {
|
|
return nil
|
|
}
|
|
if _, ok := op.Op.(*v1.Operation_OperationCheck); ok && op.UnixTimeEndMs != 0 {
|
|
lastRan = time.Unix(0, op.UnixTimeEndMs*int64(time.Millisecond))
|
|
return oplog.ErrStopIteration
|
|
}
|
|
if _, ok := op.Op.(*v1.Operation_OperationBackup); ok {
|
|
foundBackup = true
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return NeverScheduledTask, fmt.Errorf("finding last check run time: %w", err)
|
|
} else if !foundBackup {
|
|
lastRan = now
|
|
}
|
|
|
|
runAt, err := protoutil.ResolveSchedule(repo.CheckPolicy.GetSchedule(), lastRan, now)
|
|
if errors.Is(err, protoutil.ErrScheduleDisabled) {
|
|
return NeverScheduledTask, nil
|
|
} else if err != nil {
|
|
return NeverScheduledTask, fmt.Errorf("resolve schedule: %w", err)
|
|
}
|
|
|
|
return ScheduledTask{
|
|
Task: t,
|
|
RunAt: runAt,
|
|
Op: &v1.Operation{
|
|
Op: &v1.Operation_OperationCheck{},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (t *CheckTask) Run(ctx context.Context, st ScheduledTask, runner TaskRunner) error {
|
|
op := st.Op
|
|
|
|
repo, err := runner.GetRepoOrchestrator(t.RepoID())
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't get repo %q: %w", t.RepoID(), err)
|
|
}
|
|
|
|
if err := runner.ExecuteHooks(ctx, []v1.Hook_Condition{
|
|
v1.Hook_CONDITION_CHECK_START,
|
|
}, HookVars{}); err != nil {
|
|
return fmt.Errorf("check start hook: %w", err)
|
|
}
|
|
|
|
err = repo.UnlockIfAutoEnabled(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("auto unlock repo %q: %w", t.RepoID(), err)
|
|
}
|
|
|
|
opCheck := &v1.Operation_OperationCheck{
|
|
OperationCheck: &v1.OperationCheck{},
|
|
}
|
|
op.Op = opCheck
|
|
|
|
liveID, writer, err := runner.LogrefWriter()
|
|
if err != nil {
|
|
return fmt.Errorf("create logref writer: %w", err)
|
|
}
|
|
defer writer.Close()
|
|
opCheck.OperationCheck.OutputLogref = liveID
|
|
|
|
if err := runner.UpdateOperation(op); err != nil {
|
|
return fmt.Errorf("update operation: %w", err)
|
|
}
|
|
|
|
err = repo.Check(ctx, writer)
|
|
if err != nil {
|
|
runner.ExecuteHooks(ctx, []v1.Hook_Condition{
|
|
v1.Hook_CONDITION_CHECK_ERROR,
|
|
v1.Hook_CONDITION_ANY_ERROR,
|
|
}, HookVars{
|
|
Error: err.Error(),
|
|
})
|
|
|
|
return fmt.Errorf("check: %w", err)
|
|
}
|
|
|
|
if err := writer.Close(); err != nil {
|
|
return fmt.Errorf("close logref writer: %w", err)
|
|
}
|
|
|
|
if err := runner.ExecuteHooks(ctx, []v1.Hook_Condition{
|
|
v1.Hook_CONDITION_CHECK_SUCCESS,
|
|
}, HookVars{}); err != nil {
|
|
return fmt.Errorf("execute check success hooks: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|