Skip to content

Commit 443e291

Browse files
authored
Update Arango Backup Policy schedule (#458)
1 parent 2cc18ad commit 443e291

File tree

4 files changed

+119
-18
lines changed

4 files changed

+119
-18
lines changed

pkg/apis/backup/v1alpha/backup_policy_validate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (a *ArangoBackupPolicy) Validate() error {
3838
}
3939

4040
func (a *ArangoBackupPolicySpec) Validate() error {
41-
if expr, err := cron.Parse(a.Schedule); err != nil {
41+
if expr, err := cron.ParseStandard(a.Schedule); err != nil {
4242
return fmt.Errorf("error while parsing expr: %s", err.Error())
4343
} else if expr.Next(time.Now()).IsZero() {
4444
return fmt.Errorf("invalid schedule format")

pkg/backup/handlers/arango/policy/handler.go

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,19 @@ func (h *handler) processBackupPolicy(policy *backupApi.ArangoBackupPolicy) (bac
101101
}, nil
102102
}
103103

104-
if policy.Status.Scheduled.IsZero() {
105-
expr, err := cron.ParseStandard(policy.Spec.Schedule)
106-
if err != nil {
107-
h.eventRecorder.Warning(policy, policyError, "Policy Error: %s", err.Error())
104+
now := time.Now()
108105

109-
return backupApi.ArangoBackupPolicyStatus{
110-
Message: fmt.Sprintf("error while parsing expr: %s", err.Error()),
111-
}, nil
112-
}
106+
expr, err := cron.ParseStandard(policy.Spec.Schedule)
107+
if err != nil {
108+
h.eventRecorder.Warning(policy, policyError, "Policy Error: %s", err.Error())
109+
110+
return backupApi.ArangoBackupPolicyStatus{
111+
Message: fmt.Sprintf("error while parsing expr: %s", err.Error()),
112+
}, nil
113+
}
113114

114-
next := expr.Next(time.Now())
115+
if policy.Status.Scheduled.IsZero() {
116+
next := expr.Next(now)
115117

116118
return backupApi.ArangoBackupPolicyStatus{
117119
Scheduled: meta.Time{
@@ -121,7 +123,19 @@ func (h *handler) processBackupPolicy(policy *backupApi.ArangoBackupPolicy) (bac
121123
}
122124

123125
// Check if schedule is required
124-
if policy.Status.Scheduled.Unix() > time.Now().Unix() {
126+
if policy.Status.Scheduled.Unix() > now.Unix() {
127+
// check if we need to update schedule in case that string changed
128+
// in other case schedule string will be updated after scheduling objects
129+
next := expr.Next(now)
130+
131+
if next != policy.Status.Scheduled.Time {
132+
return backupApi.ArangoBackupPolicyStatus{
133+
Scheduled: meta.Time{
134+
Time: next,
135+
},
136+
}, nil
137+
}
138+
125139
return policy.Status, nil
126140
}
127141

@@ -161,13 +175,6 @@ func (h *handler) processBackupPolicy(policy *backupApi.ArangoBackupPolicy) (bac
161175
h.eventRecorder.Normal(policy, backupCreated, "Created ArangoBackup: %s/%s", b.Namespace, b.Name)
162176
}
163177

164-
expr, err := cron.ParseStandard(policy.Spec.Schedule)
165-
if err != nil {
166-
return backupApi.ArangoBackupPolicyStatus{
167-
Message: fmt.Sprintf("error while parsing expr: %s", err.Error()),
168-
}, nil
169-
}
170-
171178
next := expr.Next(time.Now())
172179

173180
h.eventRecorder.Normal(policy, rescheduled, "Rescheduled for: %s", next.String())

pkg/backup/handlers/arango/policy/handler_scheduler_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,90 @@ func Test_Scheduler_Valid_MultipleObject_Selector(t *testing.T) {
201201
require.NotNil(t, backups[1].Spec.PolicyName)
202202
require.Equal(t, policy.Name, *backups[1].Spec.PolicyName)
203203
}
204+
205+
func Test_Reschedule(t *testing.T) {
206+
// Arrange
207+
handler := newFakeHandler()
208+
209+
name := string(uuid.NewUUID())
210+
namespace := string(uuid.NewUUID())
211+
212+
selectors := map[string]string{
213+
"SELECTOR": string(uuid.NewUUID()),
214+
}
215+
216+
policy := newArangoBackupPolicy("* 13 * * *", namespace, name, selectors, backupApi.ArangoBackupTemplate{})
217+
218+
// Act
219+
createArangoBackupPolicy(t, handler, policy)
220+
221+
t.Run("First schedule", func(t *testing.T) {
222+
require.NoError(t, handler.Handle(newItemFromBackupPolicy(operation.Update, policy)))
223+
224+
// Assert
225+
newPolicy := refreshArangoBackupPolicy(t, handler, policy)
226+
require.Empty(t, newPolicy.Status.Message)
227+
228+
require.Equal(t, 13, newPolicy.Status.Scheduled.Hour())
229+
})
230+
231+
t.Run("First schedule - second iteration", func(t *testing.T) {
232+
require.NoError(t, handler.Handle(newItemFromBackupPolicy(operation.Update, policy)))
233+
234+
// Assert
235+
newPolicy := refreshArangoBackupPolicy(t, handler, policy)
236+
require.Empty(t, newPolicy.Status.Message)
237+
238+
require.Equal(t, 13, newPolicy.Status.Scheduled.Hour())
239+
})
240+
241+
t.Run("Change schedule", func(t *testing.T) {
242+
policy = refreshArangoBackupPolicy(t, handler, policy)
243+
policy.Spec.Schedule = "3 3 * * *"
244+
updateArangoBackupPolicy(t, handler, policy)
245+
246+
require.NoError(t, handler.Handle(newItemFromBackupPolicy(operation.Update, policy)))
247+
248+
// Assert
249+
newPolicy := refreshArangoBackupPolicy(t, handler, policy)
250+
require.Empty(t, newPolicy.Status.Message)
251+
252+
require.Equal(t, 3, newPolicy.Status.Scheduled.Hour())
253+
require.Equal(t, 3, newPolicy.Status.Scheduled.Minute())
254+
})
255+
}
256+
257+
func Test_Validate(t *testing.T) {
258+
acceptedSchedules := []string{
259+
"0 0 * * MON,TUE,WED,THU,FRI",
260+
"* * * * *",
261+
}
262+
263+
for _, c := range acceptedSchedules {
264+
t.Run(c, func(t *testing.T) {
265+
// Arrange
266+
handler := newFakeHandler()
267+
268+
name := string(uuid.NewUUID())
269+
namespace := string(uuid.NewUUID())
270+
271+
selectors := map[string]string{
272+
"SELECTOR": string(uuid.NewUUID()),
273+
}
274+
275+
policy := newArangoBackupPolicy(c, namespace, name, selectors, backupApi.ArangoBackupTemplate{})
276+
277+
require.NoError(t, policy.Validate())
278+
279+
// Act
280+
createArangoBackupPolicy(t, handler, policy)
281+
282+
require.NoError(t, handler.Handle(newItemFromBackupPolicy(operation.Update, policy)))
283+
284+
// Assert
285+
newPolicy := refreshArangoBackupPolicy(t, handler, policy)
286+
require.Empty(t, newPolicy.Status.Message)
287+
require.NotEmpty(t, newPolicy.Status.Scheduled)
288+
})
289+
}
290+
}

pkg/backup/handlers/arango/policy/handler_suite_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ func createArangoBackupPolicy(t *testing.T, h *handler, policies ...*backupApi.A
109109
}
110110
}
111111

112+
func updateArangoBackupPolicy(t *testing.T, h *handler, policies ...*backupApi.ArangoBackupPolicy) {
113+
for _, policy := range policies {
114+
_, err := h.client.BackupV1alpha().ArangoBackupPolicies(policy.Namespace).Update(policy)
115+
require.NoError(t, err)
116+
}
117+
}
118+
112119
func newArangoDeployment(namespace string, labels map[string]string) *database.ArangoDeployment {
113120
name := string(uuid.NewUUID())
114121
return &database.ArangoDeployment{

0 commit comments

Comments
 (0)