diff --git a/internal/adapters/runtime/kubernetes/game_room_convert.go b/internal/adapters/runtime/kubernetes/game_room_convert.go index 52fdf9e9d..355179070 100644 --- a/internal/adapters/runtime/kubernetes/game_room_convert.go +++ b/internal/adapters/runtime/kubernetes/game_room_convert.go @@ -102,11 +102,12 @@ func convertGameRoomSpec(scheduler entities.Scheduler, gameRoomName string, game func convertContainer(container game_room.Container, schedulerID, roomID string) (v1.Container, error) { podContainer := v1.Container{ - Name: container.Name, - Image: container.Image, - Command: container.Command, - Ports: []v1.ContainerPort{}, - Env: []v1.EnvVar{}, + Name: container.Name, + Image: container.Image, + ImagePullPolicy: v1.PullPolicy(container.ImagePullPolicy), + Command: container.Command, + Ports: []v1.ContainerPort{}, + Env: []v1.EnvVar{}, } for _, port := range container.Ports { diff --git a/internal/api/handlers/requestadapters/schedulers.go b/internal/api/handlers/requestadapters/schedulers.go index 4020c58ba..0a10272b8 100644 --- a/internal/api/handlers/requestadapters/schedulers.go +++ b/internal/api/handlers/requestadapters/schedulers.go @@ -532,7 +532,8 @@ func getRoomOccupancy(roomOccupancyParameters *autoscaling.RoomOccupancyParams) return nil } return &api.RoomOccupancy{ - ReadyTarget: float32(roomOccupancyParameters.ReadyTarget), + ReadyTarget: float32(roomOccupancyParameters.ReadyTarget), + DownThreshold: float32(roomOccupancyParameters.DownThreshold), } } diff --git a/internal/core/entities/scheduler_info.go b/internal/core/entities/scheduler_info.go index 783f34dab..735663fc0 100644 --- a/internal/core/entities/scheduler_info.go +++ b/internal/core/entities/scheduler_info.go @@ -25,9 +25,10 @@ package entities import "github.com/topfreegames/maestro/internal/core/entities/autoscaling" type AutoscalingInfo struct { - Enabled bool - Min int - Max int + Enabled bool + Min int + Max int + Cooldown int } type SchedulerInfo struct { @@ -101,9 +102,10 @@ func WithAutoscalingInfo(schedulerAutoscaling *autoscaling.Autoscaling) Schedule return func(schedulerInfo *SchedulerInfo) { if schedulerAutoscaling != nil { autoscalingInfo := &AutoscalingInfo{ - Enabled: schedulerAutoscaling.Enabled, - Min: schedulerAutoscaling.Min, - Max: schedulerAutoscaling.Max, + Enabled: schedulerAutoscaling.Enabled, + Min: schedulerAutoscaling.Min, + Max: schedulerAutoscaling.Max, + Cooldown: schedulerAutoscaling.Cooldown, } schedulerInfo.Autoscaling = autoscalingInfo } diff --git a/internal/core/operations/healthcontroller/executor.go b/internal/core/operations/healthcontroller/executor.go index ee6861c88..170bded58 100644 --- a/internal/core/operations/healthcontroller/executor.go +++ b/internal/core/operations/healthcontroller/executor.go @@ -174,7 +174,8 @@ func (ex *Executor) ensureDesiredAmountOfInstances(ctx context.Context, op *oper switch { case actualAmount > desiredAmount: // Need to scale down - if ex.canPerformDownscale(ctx, scheduler, logger) { + can, msg := ex.canPerformDownscale(ctx, scheduler, logger) + if can { scheduler.LastDownscaleAt = time.Now().UTC() if err := ex.schedulerStorage.UpdateScheduler(ctx, scheduler); err != nil { logger.Error("error updating scheduler", zap.Error(err)) @@ -191,7 +192,7 @@ func (ex *Executor) ensureDesiredAmountOfInstances(ctx context.Context, op *oper msgToAppend = fmt.Sprintf("created operation (id: %s) to remove %v rooms.", removeOperation.ID, removeAmount) } else { tookAction = false - msgToAppend = "cannot scale down because cool down period has not passed yet" + msgToAppend = msg } case actualAmount < desiredAmount: // Need to scale up addAmount := desiredAmount - actualAmount @@ -329,11 +330,11 @@ func (ex *Executor) setTookAction(def *Definition, tookAction bool) { def.TookAction = &tookAction } -func (ex *Executor) canPerformDownscale(ctx context.Context, scheduler *entities.Scheduler, logger *zap.Logger) bool { +func (ex *Executor) canPerformDownscale(ctx context.Context, scheduler *entities.Scheduler, logger *zap.Logger) (bool, string) { can, err := ex.autoscaler.CanDownscale(ctx, scheduler) if err != nil { logger.Error("error checking if scheduler can downscale", zap.Error(err)) - return can + return can, err.Error() } cooldown := 0 @@ -343,5 +344,17 @@ func (ex *Executor) canPerformDownscale(ctx context.Context, scheduler *entities cooldownDuration := time.Duration(cooldown) * time.Second waitingCooldown := scheduler.LastDownscaleAt.Add(cooldownDuration).After(time.Now().UTC()) - return can && !waitingCooldown + if !can { + message := fmt.Sprintf("scheduler %s can't downscale, occupation is above the threshold", scheduler.Name) + logger.Info(message) + return false, message + } + + if can && waitingCooldown { + message := fmt.Sprintf("scheduler %s can downscale, but cooldown period has not passed yet", scheduler.Name) + logger.Info(message) + return false, message + } + + return can && !waitingCooldown, "ok" } diff --git a/internal/core/services/autoscaler/policies/roomoccupancy/policy_test.go b/internal/core/services/autoscaler/policies/roomoccupancy/policy_test.go index 0ddcbed24..f100e2411 100644 --- a/internal/core/services/autoscaler/policies/roomoccupancy/policy_test.go +++ b/internal/core/services/autoscaler/policies/roomoccupancy/policy_test.go @@ -309,7 +309,7 @@ func TestCanDownscale(t *testing.T) { assert.Truef(t, allow, "downscale should be allowed") }) - t.Run("it is expected to allow downscale when occupation is equal the threshold", func(t *testing.T) { + t.Run("it is expected to allow downscale when occupation below the threshold", func(t *testing.T) { readyTarget := float64(0.5) downThreshold := float64(0.6) occupiedRooms := 60 @@ -419,7 +419,7 @@ func TestCanDownscale(t *testing.T) { _, err := policy.CanDownscale(policyParams, schedulerState) assert.EqualError(t, err, "Downscale threshold must be between 0 and 1") }) - t.Run("when ready target is 0", func(t *testing.T) { + t.Run("when down threshold is 0", func(t *testing.T) { downThreshold := float64(0.0) occupiedRooms := 10 readyRooms := 10 @@ -438,7 +438,7 @@ func TestCanDownscale(t *testing.T) { _, err := policy.CanDownscale(policyParams, schedulerState) assert.EqualError(t, err, "Downscale threshold must be between 0 and 1") }) - t.Run("when ready target is lower than 0", func(t *testing.T) { + t.Run("when down threshold is lower than 0", func(t *testing.T) { downThreshold := float64(-0.1) occupiedRooms := 10 readyRooms := 10