Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: compact the scheduling UI and use an enum for clock configuration #452

Merged
merged 2 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
rework relative scheduling to use an enum
  • Loading branch information
garethgeorge committed Sep 4, 2024
commit 7df8ef932b6371091516787fb7fe507c7f3112cb
455 changes: 233 additions & 222 deletions gen/go/v1/config.pb.go

Large diffs are not rendered by default.

23 changes: 2 additions & 21 deletions internal/config/migrations/003relativescheduling.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,6 @@ import (
v1 "github.com/garethgeorge/backrest/gen/go/v1"
)

func convertToRelativeSchedule(sched *v1.Schedule) {
switch s := sched.GetSchedule().(type) {
case *v1.Schedule_MaxFrequencyDays:
sched.Schedule = &v1.Schedule_MinDaysSinceLastRun{
MinDaysSinceLastRun: s.MaxFrequencyDays,
}
case *v1.Schedule_MaxFrequencyHours:
sched.Schedule = &v1.Schedule_MinHoursSinceLastRun{
MinHoursSinceLastRun: s.MaxFrequencyHours,
}
case *v1.Schedule_Cron:
sched.Schedule = &v1.Schedule_CronSinceLastRun{
CronSinceLastRun: s.Cron,
}
default:
// do nothing
}
}

func migration003RelativeScheduling(config *v1.Config) {
// loop over plans and examine prune policy's
for _, repo := range config.Repos {
Expand All @@ -32,11 +13,11 @@ func migration003RelativeScheduling(config *v1.Config) {
}

if schedule := repo.GetPrunePolicy().GetSchedule(); schedule != nil {
convertToRelativeSchedule(schedule)
schedule.Clock = v1.Schedule_CLOCK_LAST_RUN_TIME
}

if schedule := repo.GetCheckPolicy().GetSchedule(); schedule != nil {
convertToRelativeSchedule(schedule)
schedule.Clock = v1.Schedule_CLOCK_LAST_RUN_TIME
}
}
}
94 changes: 4 additions & 90 deletions internal/config/migrations/003relativescheduling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func Test003Migration(t *testing.T) {
config := &v1.Config{
Repos: []*v1.Repo{
{
Id: "prune daily",
Id: "prune",
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MaxFrequencyDays{
Expand All @@ -27,98 +27,12 @@ func Test003Migration(t *testing.T) {
},
},
},
{
Id: "prune hourly",
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MaxFrequencyHours{
MaxFrequencyHours: 1,
},
},
},
CheckPolicy: &v1.CheckPolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MaxFrequencyHours{
MaxFrequencyHours: 1,
},
},
},
},
{
Id: "prune cron",
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_Cron{
Cron: "0 0 * * *",
},
},
},
CheckPolicy: &v1.CheckPolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_Cron{
Cron: "0 0 * * *",
},
},
},
},
},
}

want := &v1.Config{
Repos: []*v1.Repo{
{
Id: "prune daily",
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinDaysSinceLastRun{
MinDaysSinceLastRun: 1,
},
},
},
CheckPolicy: &v1.CheckPolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinDaysSinceLastRun{
MinDaysSinceLastRun: 1,
},
},
},
},
{
Id: "prune hourly",
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinHoursSinceLastRun{
MinHoursSinceLastRun: 1,
},
},
},
CheckPolicy: &v1.CheckPolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinHoursSinceLastRun{
MinHoursSinceLastRun: 1,
},
},
},
},
{
Id: "prune cron",
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_CronSinceLastRun{
CronSinceLastRun: "0 0 * * *",
},
},
},
CheckPolicy: &v1.CheckPolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_CronSinceLastRun{
CronSinceLastRun: "0 0 * * *",
},
},
},
},
},
}
want := proto.Clone(config).(*v1.Config)
want.Repos[0].PrunePolicy.Schedule.Clock = v1.Schedule_CLOCK_LAST_RUN_TIME
want.Repos[0].CheckPolicy.Schedule.Clock = v1.Schedule_CLOCK_LAST_RUN_TIME

migration003RelativeScheduling(config)

Expand Down
2 changes: 1 addition & 1 deletion internal/orchestrator/orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (o *Orchestrator) ScheduleDefaultTasks(config *v1.Config) error {
}
}

zap.L().Info("reset task queue, scheduling new task set.")
zap.L().Info("reset task queue, scheduling new task set", zap.String("timezone", time.Now().Location().String()))

// Requeue tasks that are affected by the config change.
if err := o.ScheduleTask(tasks.NewCollectGarbageTask(), tasks.TaskPriorityDefault); err != nil {
Expand Down
27 changes: 17 additions & 10 deletions internal/orchestrator/tasks/scheduling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,18 @@ func TestScheduling(t *testing.T) {
Id: "repo-relative",
CheckPolicy: &v1.CheckPolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinHoursSinceLastRun{
MinHoursSinceLastRun: 1,
Schedule: &v1.Schedule_MaxFrequencyHours{
MaxFrequencyHours: 1,
},
Clock: v1.Schedule_CLOCK_LAST_RUN_TIME,
},
},
PrunePolicy: &v1.PrunePolicy{
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinHoursSinceLastRun{
MinHoursSinceLastRun: 1,
Schedule: &v1.Schedule_MaxFrequencyHours{
MaxFrequencyHours: 1,
},
Clock: v1.Schedule_CLOCK_LAST_RUN_TIME,
},
},
},
Expand All @@ -64,14 +66,16 @@ func TestScheduling(t *testing.T) {
Schedule: &v1.Schedule_Cron{
Cron: "0 0 * * *", // every day at midnight
},
Clock: v1.Schedule_CLOCK_LOCAL,
},
},
{
Id: "plan-cron-since-last-run",
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_CronSinceLastRun{
CronSinceLastRun: "0 0 * * *", // every day at midnight
Schedule: &v1.Schedule_Cron{
Cron: "0 0 * * *", // every day at midnight
},
Clock: v1.Schedule_CLOCK_LAST_RUN_TIME,
},
},
{
Expand All @@ -81,15 +85,17 @@ func TestScheduling(t *testing.T) {
Schedule: &v1.Schedule_MaxFrequencyDays{
MaxFrequencyDays: 1,
},
Clock: v1.Schedule_CLOCK_LAST_RUN_TIME,
},
},
{
Id: "plan-min-days-since-last-run",
Repo: "repo1",
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinDaysSinceLastRun{
MinDaysSinceLastRun: 1,
Schedule: &v1.Schedule_MaxFrequencyDays{
MaxFrequencyDays: 1,
},
Clock: v1.Schedule_CLOCK_LAST_RUN_TIME,
},
},
{
Expand All @@ -105,9 +111,10 @@ func TestScheduling(t *testing.T) {
Id: "plan-min-hours-since-last-run",
Repo: "repo1",
Schedule: &v1.Schedule{
Schedule: &v1.Schedule_MinHoursSinceLastRun{
MinHoursSinceLastRun: 1,
Schedule: &v1.Schedule_MaxFrequencyHours{
MaxFrequencyHours: 1,
},
Clock: v1.Schedule_CLOCK_LAST_RUN_TIME,
},
},
},
Expand Down
45 changes: 16 additions & 29 deletions internal/protoutil/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,32 @@ var ErrScheduleDisabled = errors.New("never")
// ResolveSchedule resolves a schedule to the next time it should run based on last execution.
// note that this is different from backup behavior which is always relative to the current time.
func ResolveSchedule(sched *v1.Schedule, lastRan time.Time, curTime time.Time) (time.Time, error) {

var t time.Time
switch sched.GetClock() {
case v1.Schedule_CLOCK_LOCAL:
t = curTime.Local()
case v1.Schedule_CLOCK_UTC:
t = curTime.UTC()
case v1.Schedule_CLOCK_LAST_RUN_TIME:
t = lastRan
default:
return time.Time{}, fmt.Errorf("unknown clock type: %T", s)
}

switch s := sched.GetSchedule().(type) {
case *v1.Schedule_Disabled:
return time.Time{}, ErrScheduleDisabled
case *v1.Schedule_MaxFrequencyDays:
return curTime.Add(time.Duration(s.MaxFrequencyDays) * 24 * time.Hour), nil
return t.Add(time.Duration(s.MaxFrequencyDays) * 24 * time.Hour), nil
case *v1.Schedule_MaxFrequencyHours:
return curTime.Add(time.Duration(s.MaxFrequencyHours) * time.Hour), nil
return t.Add(time.Duration(s.MaxFrequencyHours) * time.Hour), nil
case *v1.Schedule_Cron:
cron, err := cronexpr.ParseInLocation(s.Cron, time.Now().Location().String())
if err != nil {
return time.Time{}, fmt.Errorf("parse cron %q: %w", s.Cron, err)
}
return cron.Next(curTime), nil
case *v1.Schedule_MinHoursSinceLastRun:
return lastRan.Add(time.Duration(s.MinHoursSinceLastRun) * time.Hour), nil
case *v1.Schedule_MinDaysSinceLastRun:
return lastRan.Add(time.Duration(s.MinDaysSinceLastRun) * 24 * time.Hour), nil
case *v1.Schedule_CronSinceLastRun:
cron, err := cronexpr.ParseInLocation(s.CronSinceLastRun, time.Now().Location().String())
if err != nil {
return time.Time{}, fmt.Errorf("parse cron %q: %w", s.CronSinceLastRun, err)
}
return cron.Next(lastRan), nil
return cron.Next(t), nil
default:
return time.Time{}, fmt.Errorf("unknown schedule type: %T", s)
}
Expand All @@ -60,22 +63,6 @@ func ValidateSchedule(sched *v1.Schedule) error {
if err != nil {
return fmt.Errorf("invalid cron %q: %w", s.Cron, err)
}
case *v1.Schedule_MinHoursSinceLastRun:
if s.MinHoursSinceLastRun < 1 {
return errors.New("invalid min hours since last run")
}
case *v1.Schedule_MinDaysSinceLastRun:
if s.MinDaysSinceLastRun < 1 {
return errors.New("invalid min days since last run")
}
case *v1.Schedule_CronSinceLastRun:
if s.CronSinceLastRun == "" {
return errors.New("empty cron expression")
}
_, err := cronexpr.ParseInLocation(s.CronSinceLastRun, time.Now().Location().String())
if err != nil {
return fmt.Errorf("invalid cron %q: %w", s.CronSinceLastRun, err)
}
case *v1.Schedule_Disabled:
if !s.Disabled {
return errors.New("disabled boolean must be set to true")
Expand Down
11 changes: 8 additions & 3 deletions proto/v1/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,15 @@ message Schedule {
string cron = 2 [json_name="cron"]; // cron expression describing the schedule.
int32 maxFrequencyDays = 3 [json_name="maxFrequencyDays"]; // max frequency of runs in days.
int32 maxFrequencyHours = 4 [json_name="maxFrequencyHours"]; // max frequency of runs in hours.
string cronSinceLastRun = 100 [json_name="cronSinceLastRun"]; // cron expression to run since the last run.
int32 minHoursSinceLastRun = 101 [json_name="minHoursSinceLastRun"]; // max hours since the last run.
int32 minDaysSinceLastRun = 102 [json_name="minDaysSinceLastRun"]; // max days since the last run.
}

enum Clock {
CLOCK_LOCAL = 0;
CLOCK_UTC = 1;
CLOCK_LAST_RUN_TIME = 2;
}

Clock clock = 5 [json_name="clock"]; // clock to use for scheduling.
}

message Hook {
Expand Down
Loading