Files
backrest/internal/orchestrator/taskforget.go

121 lines
3.2 KiB
Go

package orchestrator
import (
"context"
"fmt"
"time"
v1 "github.com/garethgeorge/backrest/gen/go/v1"
"github.com/garethgeorge/backrest/internal/hook"
"github.com/garethgeorge/backrest/internal/oplog/indexutil"
"github.com/hashicorp/go-multierror"
"go.uber.org/zap"
)
// ForgetTask tracks a forget operation.
type ForgetTask struct {
name string
TaskWithOperation
plan *v1.Plan
linkSnapshot string // snapshot to link the task to.
at *time.Time
}
var _ Task = &ForgetTask{}
func NewOneoffForgetTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time) *ForgetTask {
return &ForgetTask{
TaskWithOperation: TaskWithOperation{
orch: orchestrator,
},
plan: plan,
at: &at,
linkSnapshot: linkSnapshot,
}
}
func (t *ForgetTask) Name() string {
return fmt.Sprintf("forget for plan %q", t.plan.Id)
}
func (t *ForgetTask) Next(now time.Time) *time.Time {
ret := t.at
if ret != nil {
t.at = nil
if err := t.setOperation(&v1.Operation{
PlanId: t.plan.Id,
RepoId: t.plan.Repo,
SnapshotId: t.linkSnapshot,
UnixTimeStartMs: timeToUnixMillis(*ret),
Status: v1.OperationStatus_STATUS_PENDING,
Op: &v1.Operation_OperationForget{},
}); err != nil {
zap.S().Errorf("task %v failed to add operation to oplog: %v", t.Name(), err)
return nil
}
}
return ret
}
func (t *ForgetTask) Run(ctx context.Context) error {
if err := t.runWithOpAndContext(ctx, func(ctx context.Context, op *v1.Operation) error {
forgetOp := &v1.Operation_OperationForget{
OperationForget: &v1.OperationForget{},
}
op.Op = forgetOp
var err error
repo, err := t.orch.GetRepo(t.plan.Repo)
if err != nil {
return fmt.Errorf("get repo %q: %w", t.plan.Repo, err)
}
err = repo.UnlockIfAutoEnabled(ctx)
if err != nil {
return fmt.Errorf("auto unlock repo %q: %w", t.plan.Repo, err)
}
forgot, err := repo.Forget(ctx, t.plan)
if err != nil {
return fmt.Errorf("forget: %w", err)
}
forgetOp.OperationForget.Forget = append(forgetOp.OperationForget.Forget, forgot...)
forgetOp.OperationForget.Policy = t.plan.Retention
var ops []*v1.Operation
for _, forgot := range forgot {
if e := t.orch.OpLog.ForEachBySnapshotId(forgot.Id, indexutil.CollectAll(), func(op *v1.Operation) error {
ops = append(ops, op)
return nil
}); e != nil {
err = multierror.Append(err, fmt.Errorf("cleanup snapshot %v: %w", forgot.Id, e))
}
}
for _, op := range ops {
if indexOp, ok := op.Op.(*v1.Operation_OperationIndexSnapshot); ok {
indexOp.OperationIndexSnapshot.Forgot = true
if e := t.orch.OpLog.Update(op); err != nil {
err = multierror.Append(err, fmt.Errorf("mark index snapshot %v as forgotten: %w", op.Id, e))
continue
}
}
}
if len(forgot) > 0 {
t.orch.ScheduleTask(NewOneoffPruneTask(t.orch, t.plan, time.Now(), false), TaskPriorityPrune)
}
return err
}); err != nil {
repo, _ := t.orch.GetRepo(t.plan.Repo)
t.orch.hookExecutor.ExecuteHooks(repo.Config(), t.plan, t.linkSnapshot, []v1.Hook_Condition{
v1.Hook_CONDITION_ANY_ERROR,
}, hook.HookVars{
Task: t.Name(),
Error: err.Error(),
})
}
return nil
}