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

add InitialCooldownPeriod for ScaledObjects #5478

Merged
merged 13 commits into from
Apr 12, 2024
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio

- **General**: Provide capability to filter CloudEvents ([#3533](https://github.com/kedacore/keda/issues/3533))
- **NATS Scaler**: Add TLS authentication ([#2296](https://github.com/kedacore/keda/issues/2296))
- **ScaledObject**: Ability to specify `initialCooldownPeriod` ([#5008](https://github.com/kedacore/keda/issues/5008))



#### Experimental

Expand Down
2 changes: 2 additions & 0 deletions apis/keda/v1alpha1/scaledobject_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ type ScaledObjectSpec struct {
Triggers []ScaleTriggers `json:"triggers"`
// +optional
Fallback *Fallback `json:"fallback,omitempty"`
// +optional
InitialCooldownPeriod int32 `json:"initialCooldownPeriod,omitempty"`
}

// Fallback is the spec for fallback options
Expand Down
3 changes: 3 additions & 0 deletions config/crd/bases/keda.sh_scaledobjects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ spec:
idleReplicaCount:
format: int32
type: integer
initialCooldownPeriod:
format: int32
type: integer
maxReplicaCount:
format: int32
type: integer
Expand Down
12 changes: 9 additions & 3 deletions pkg/scaling/executor/scale_scaledobjects.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,18 @@ func (e *scaleExecutor) scaleToZeroOrIdle(ctx context.Context, logger logr.Logge
cooldownPeriod = time.Second * time.Duration(defaultCooldownPeriod)
}

initialCooldownPeriod := time.Second * time.Duration(scaledObject.Spec.InitialCooldownPeriod)

// If the ScaledObject was just created,CreationTimestamp is zero, set the CreationTimestamp to now
if scaledObject.ObjectMeta.CreationTimestamp.IsZero() {
scaledObject.ObjectMeta.CreationTimestamp = metav1.NewTime(time.Now())
}

// LastActiveTime can be nil if the ScaleTarget was scaled outside of KEDA.
// In this case we will ignore the cooldown period and scale it down
if scaledObject.Status.LastActiveTime == nil ||
scaledObject.Status.LastActiveTime.Add(cooldownPeriod).Before(time.Now()) {
if (scaledObject.Status.LastActiveTime == nil && scaledObject.ObjectMeta.CreationTimestamp.Add(initialCooldownPeriod).Before(time.Now())) || (scaledObject.Status.LastActiveTime != nil &&
scaledObject.Status.LastActiveTime.Add(cooldownPeriod).Before(time.Now())) {
// or last time a trigger was active was > cooldown period, so scale in.

idleValue, scaleToReplicas := getIdleOrMinimumReplicaCount(scaledObject)

currentReplicas, err := e.updateScaleOnScaleTarget(ctx, scaledObject, scale, scaleToReplicas)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//go:build e2e
// +build e2e

package initial_delay_cooldownperiod_test

import (
"fmt"
"strconv"
"testing"
"time"

"github.com/stretchr/testify/assert"

. "github.com/kedacore/keda/v2/tests/helper"
)

const (
testName = "initial-delay-cooldownperiod-test"
)

var (
testNamespace = fmt.Sprintf("%s-ns", testName)
deploymentName = fmt.Sprintf("%s-deployment", testName)
scaledObjectName = fmt.Sprintf("%s-so", testName)

now = time.Now().Local()
start = (now.Minute() + 10) % 60
end = (start + 1) % 60
)

type templateData struct {
TestNamespace string
DeploymentName string
ScaledObjectName string
StartMin string
EndMin string
}

const (
deploymentTemplate = `
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{.DeploymentName}}
namespace: {{.TestNamespace}}
labels:
deploy: {{.DeploymentName}}
spec:
replicas: 1
selector:
matchLabels:
pod: {{.DeploymentName}}
template:
metadata:
labels:
pod: {{.DeploymentName}}
spec:
containers:
- name: nginx
image: 'nginxinc/nginx-unprivileged'
`

scaledObjectTemplate = `
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: {{.ScaledObjectName}}
namespace: {{.TestNamespace}}
spec:
scaleTargetRef:
name: {{.DeploymentName}}
cooldownPeriod: 5
minReplicaCount: 0
initialCooldownPeriod: 120
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 15
triggers:
- type: cron
metadata:
timezone: Etc/UTC
start: {{.StartMin}} * * * *
end: {{.EndMin}} * * * *
desiredReplicas: '0'
`
)

func TestScaler(t *testing.T) {
// setup
t.Log("--- setting up ---")

// Create kubernetes resources
kc := GetKubernetesClient(t)
data, templates := getTemplateData()

CreateKubernetesResources(t, kc, testNamespace, data, templates)
assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, deploymentName, testNamespace, 1, 60, 1),
"replica count should be %d after 1 minute", 1)
t.Log("--- Waiting for some time to ensure deployment replica count doesn't change ---")
AssertReplicaCountNotChangeDuringTimePeriod(t, kc, deploymentName, testNamespace, 1, 90)
t.Log("--- scale to 0 replicas ---")
assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, deploymentName, testNamespace, 0, 120, 1),
"replica count should be %d after 2 minute", 0) // Assert that the workload is scaled to zero after the initial cooldown period

DeleteKubernetesResources(t, testNamespace, data, templates)
}
func getTemplateData() (templateData, []Template) {
return templateData{
TestNamespace: testNamespace,
DeploymentName: deploymentName,
ScaledObjectName: scaledObjectName,
StartMin: strconv.Itoa(start),
EndMin: strconv.Itoa(end),
}, []Template{
{Name: "deploymentTemplate", Config: deploymentTemplate},
{Name: "scaledObjectTemplate", Config: scaledObjectTemplate},
}
}
Loading