Skip to content

Commit 3ac13e3

Browse files
committed
Add schedule and chain policy tests
1 parent fcc966c commit 3ac13e3

File tree

4 files changed

+272
-347
lines changed

4 files changed

+272
-347
lines changed

pkg/apis/autoscaling/v1/fleetautoscaler.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,9 @@ func (c *ChainPolicy) ValidateChainPolicy(fldPath *field.Path) field.ErrorList {
581581
seenIDs[entry.ID] = true
582582
}
583583
// Ensure that chain entry has a policy
584-
hasValidPolicy := entry.Buffer == nil && entry.Webhook == nil && entry.Counter == nil && entry.List == nil && entry.Schedule == nil
585-
if entry.Type == "" || hasValidPolicy {
586-
allErrs = append(allErrs, field.Required(fldPath.Index(i), "policy is missing"))
587-
}
588-
// Ensure the chain entry's policy is not a chain policy (to avoid nested chain policies)
589-
if entry.Chain != nil {
590-
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), entry.FleetAutoscalerPolicy.Type, "chain policy cannot be used in chain policy"))
584+
hasValidPolicy := entry.Buffer != nil || entry.Webhook != nil || entry.Counter != nil || entry.List != nil || entry.Schedule != nil
585+
if entry.Type == "" || !hasValidPolicy {
586+
allErrs = append(allErrs, field.Required(fldPath.Index(i), "valid policy is missing"))
591587
}
592588
// Validate the chain entry's policy
593589
allErrs = append(allErrs, entry.FleetAutoscalerPolicy.ValidatePolicy(fldPath.Index(i).Child("policy"))...)

pkg/apis/autoscaling/v1/fleetautoscaler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ func TestFleetAutoscalerChainValidateUpdate(t *testing.T) {
586586
}
587587
}),
588588
featureFlags: string(runtime.FeatureScheduledAutoscaler) + "=true",
589-
wantLength: 2,
589+
wantLength: 1,
590590
wantField: "spec.policy.chain[1]",
591591
},
592592
"invalid nested policy format": {

pkg/fleetautoscalers/fleetautoscalers.go

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func computeDesiredFleetSize(pol autoscalingv1.FleetAutoscalerPolicy, f *agonesv
6565
case autoscalingv1.SchedulePolicyType:
6666
return applySchedulePolicy(pol.Schedule, f, gameServerLister, nodeCounts, time.Now())
6767
case autoscalingv1.ChainPolicyType:
68-
return applyChainPolicy(pol.Chain, f, gameServerLister, nodeCounts)
68+
return applyChainPolicy(pol.Chain, f, gameServerLister, nodeCounts, time.Now())
6969
}
7070

7171
return 0, false, errors.New("wrong policy type, should be one of: Buffer, Webhook, Counter, List")
@@ -373,95 +373,96 @@ func applySchedulePolicy(s *autoscalingv1.SchedulePolicy, f *agonesv1.Fleet, gam
373373
return 0, false, errors.Errorf("cannot apply SchedulePolicy unless feature flag %s is enabled", runtime.FeatureScheduledAutoscaler)
374374
}
375375

376-
fmt.Println("Above")
377376
if isScheduleActive(s, currentTime) {
378-
fmt.Println("Inside")
379377
return computeDesiredFleetSize(s.Policy, f, gameServerLister, nodeCounts)
380378
}
381379

382-
var replicas int32
383-
fmt.Println("Outside")
384-
if f != nil {
385-
replicas = f.Status.Replicas
386-
}
387-
388-
return replicas, false, nil
380+
// If the schedule wasn't active then return the current replica amount of the fleet
381+
return f.Status.Replicas, false, errors.Errorf("inactive schedule, policy not applicable at this time: %s", currentTime)
389382
}
390383

391-
func applyChainPolicy(c autoscalingv1.ChainPolicy, f *agonesv1.Fleet, gameServerLister listeragonesv1.GameServerLister, nodeCounts map[string]gameservers.NodeCount) (int32, bool, error) {
384+
func applyChainPolicy(c autoscalingv1.ChainPolicy, f *agonesv1.Fleet, gameServerLister listeragonesv1.GameServerLister, nodeCounts map[string]gameservers.NodeCount, currentTime time.Time) (int32, bool, error) {
392385
// Ensure the scheduled autoscaler feature gate is enabled
393386
if !runtime.FeatureEnabled(runtime.FeatureScheduledAutoscaler) {
394387
return 0, false, errors.Errorf("cannot apply ChainPolicy unless feature flag %s is enabled", runtime.FeatureScheduledAutoscaler)
395388
}
396389

390+
replicas := f.Status.Replicas
391+
var limited bool
392+
var err error
393+
397394
// Loop over all entries in the chain
398395
for _, entry := range c {
399396
switch entry.Type {
400397
case autoscalingv1.SchedulePolicyType:
401-
schedRep, schedLim, schedErr := applySchedulePolicy(entry.Schedule, f, gameServerLister, nodeCounts, time.Now())
402-
// If the schedule is active and no error was returned from the policy, then return the replicas, limited and error
403-
if isScheduleActive(entry.Schedule, time.Now()) && schedErr == nil {
404-
return schedRep, schedLim, nil
398+
replicas, limited, err = applySchedulePolicy(entry.Schedule, f, gameServerLister, nodeCounts, currentTime)
399+
// If no error was returned from the schedule policy (schedule is active and/or webhook policy within schedule was successful), then return the values given
400+
if err == nil {
401+
return replicas, limited, nil
405402
}
406403
case autoscalingv1.WebhookPolicyType:
407-
webhookRep, webhookLim, webhookErr := applyWebhookPolicy(entry.Webhook, f)
408-
if webhookErr == nil {
409-
return webhookRep, webhookLim, nil
404+
replicas, limited, err = applyWebhookPolicy(entry.Webhook, f)
405+
// If no error was returned from the webhook policy, then return the values given
406+
if err == nil {
407+
return replicas, limited, nil
410408
}
411409
default:
410+
// Every other policy type we just want to compute the desired fleet and return it
412411
return computeDesiredFleetSize(entry.FleetAutoscalerPolicy, f, gameServerLister, nodeCounts)
413412
}
414413
}
415414

416-
return f.Status.Replicas, false, nil
415+
// Fall off the chain
416+
return replicas, limited, err
417417
}
418418

419419
// isScheduleActive checks if a chain entry's is active and returns a boolean, true if active, false otherwise
420420
func isScheduleActive(s *autoscalingv1.SchedulePolicy, currentTime time.Time) bool {
421-
// var now = time.Now()
422-
// fmt.Printf("Time Now Is: %s\n", now)
423-
cronDelta := time.Minute * -2
421+
// When retrieving the next available cronTime it returns the next minute e.g. if you want a policy to run every minute (all the time)
422+
// If the current time is 1:00, the next start time returned will be 1:01, thus we use a cronDelta of 90 seconds to
423+
// adjust to the minute delta (since the next start time will always be ahead of the current time by 1 minute when the schedule is active).
424+
cronDelta := (time.Minute * -1) + (time.Second * -30)
424425

425-
// If a start time is present and the current time is before the start time, the schedule is inactive so return false
426+
// If the current time is before the start time, the schedule is inactive so return false
426427
startTime := s.Between.Start.Time
427-
if !startTime.IsZero() && currentTime.Before(startTime) {
428-
fmt.Println("ONE")
428+
if currentTime.Before(startTime) {
429429
return false
430430
}
431431

432432
// If an end time is present and the current time is after the end time, the schedule is inactive so return false
433433
endTime := s.Between.End.Time
434434
if !endTime.IsZero() && currentTime.After(endTime) {
435-
fmt.Println("TWO")
436435
return false
437436
}
438437

439438
// If no startCron field is specified, then it's automatically true (duration is no longer relevant since we're always running)
440439
if s.ActivePeriod.StartCron == "" {
441-
fmt.Println("THREE")
442440
return true
443441
}
444442

445443
location, _ := time.LoadLocation(s.ActivePeriod.Timezone)
446444
startCron, _ := cron.ParseStandard(s.ActivePeriod.StartCron)
447-
nextStartTime := startCron.Next(currentTime.In(location)).Add(cronDelta)
448-
duration, err := time.ParseDuration(s.ActivePeriod.Duration)
445+
duration, _ := time.ParseDuration(s.ActivePeriod.Duration)
446+
nextStartTime := startCron.Next(currentTime.In(location))
449447

450448
// If there's an err, then the duration field is empty, meaning duration is indefinite
451-
if err != nil {
449+
if s.ActivePeriod.Duration == "" {
452450
duration = 0 // Indefinite duration if not set
453451
}
454452

455453
// If the current time is after the next start time, and the duration is indefinite or the current time is before the next start time + duration,
456-
// then return true
457-
fmt.Printf("Current Time: %s\n", currentTime)
458-
fmt.Printf("Next Start Time: %s\n", nextStartTime)
459-
if currentTime.After(nextStartTime) && (duration == 0 || currentTime.Before(nextStartTime.Add(duration))) {
460-
fmt.Println("FOUR")
454+
// then return true.
455+
// e.g.
456+
// startCron = * * * * *
457+
// currentTime = 2024-08-01T14:30:00Z // 2:30 PM UTC on August 1, 2024
458+
// nextStartTime = 2024-08-01T14:31:00Z // 2:31 PM UTC on August 1, 2024
459+
// duration = 1 hour
460+
// cronDelta = 1 Minute Delta 30 seconds
461+
// This would return true since the currentTime > nextStartTime - cronDelta AND the currentTime < nextStartTime + duration.
462+
if currentTime.After(nextStartTime.Add(cronDelta)) && (duration == 0 || currentTime.Before(nextStartTime.Add(duration))) {
461463
return true
462464
}
463465

464-
fmt.Println("FIVE")
465466
return false
466467
}
467468

0 commit comments

Comments
 (0)