diff --git a/apis/keda/v1alpha1/condition_types.go b/apis/keda/v1alpha1/condition_types.go index 9e8709fa6c1..59faec27a49 100644 --- a/apis/keda/v1alpha1/condition_types.go +++ b/apis/keda/v1alpha1/condition_types.go @@ -32,6 +32,8 @@ const ( ConditionActive ConditionType = "Active" // ConditionFallback specifies that the resource has a fallback active. ConditionFallback ConditionType = "Fallback" + // ConditionPaused specifies that the resource is paused. + ConditionPaused ConditionType = "Paused" ) const ( @@ -39,6 +41,8 @@ const ( ScaledObjectConditionReadySucccesReason = "ScaledObjectReady" // ScaledObjectConditionReadySuccessMessage defines the default Message for correct ScaledObject ScaledObjectConditionReadySuccessMessage = "ScaledObject is defined correctly and is ready for scaling" + // ScaledObjectConditionPausedReason defines the default Reason for paused ScaledObject + ScaledObjectConditionPausedReason = "ScaledObjectPaused" // ScaledObjectConditionPausedMessage defines the default Message for paused ScaledObject ScaledObjectConditionPausedMessage = "ScaledObject is paused" ) @@ -72,6 +76,7 @@ func (c *Conditions) AreInitialized() bool { foundReady := false foundActive := false foundFallback := false + foundPaused := false if *c != nil { for _, condition := range *c { if condition.Type == ConditionReady { @@ -91,14 +96,20 @@ func (c *Conditions) AreInitialized() bool { break } } + for _, condition := range *c { + if condition.Type == ConditionPaused { + foundPaused = true + break + } + } } - return foundReady && foundActive && foundFallback + return foundReady && foundActive && foundFallback && foundPaused } // GetInitializedConditions returns Conditions initialized to the default -> Status: Unknown func GetInitializedConditions() *Conditions { - return &Conditions{{Type: ConditionReady, Status: metav1.ConditionUnknown}, {Type: ConditionActive, Status: metav1.ConditionUnknown}, {Type: ConditionFallback, Status: metav1.ConditionUnknown}} + return &Conditions{{Type: ConditionReady, Status: metav1.ConditionUnknown}, {Type: ConditionActive, Status: metav1.ConditionUnknown}, {Type: ConditionFallback, Status: metav1.ConditionUnknown}, {Type: ConditionPaused, Status: metav1.ConditionUnknown}} } // IsTrue is true if the condition is True @@ -149,6 +160,14 @@ func (c *Conditions) SetFallbackCondition(status metav1.ConditionStatus, reason c.setCondition(ConditionFallback, status, reason, message) } +// SetPausedCondition modifies Paused Condition according to input parameters +func (c *Conditions) SetPausedCondition(status metav1.ConditionStatus, reason string, message string) { + if *c == nil { + c = GetInitializedConditions() + } + c.setCondition(ConditionPaused, status, reason, message) +} + // GetActiveCondition returns Condition of type Active func (c *Conditions) GetActiveCondition() Condition { if *c == nil { @@ -165,7 +184,7 @@ func (c *Conditions) GetReadyCondition() Condition { return c.getCondition(ConditionReady) } -// GetFallbackCondition returns Condition of type Ready +// GetFallbackCondition returns Condition of type Fallback func (c *Conditions) GetFallbackCondition() Condition { if *c == nil { c = GetInitializedConditions() @@ -173,6 +192,14 @@ func (c *Conditions) GetFallbackCondition() Condition { return c.getCondition(ConditionFallback) } +// GetPausedCondition returns Condition of type Paused +func (c *Conditions) GetPausedCondition() Condition { + if *c == nil { + c = GetInitializedConditions() + } + return c.getCondition(ConditionPaused) +} + func (c Conditions) getCondition(conditionType ConditionType) Condition { for i := range c { if c[i].Type == conditionType { diff --git a/controllers/keda/scaledobject_controller.go b/controllers/keda/scaledobject_controller.go index f22a0b57814..1245f76e4f7 100644 --- a/controllers/keda/scaledobject_controller.go +++ b/controllers/keda/scaledobject_controller.go @@ -203,13 +203,20 @@ func (r *ScaledObjectReconciler) reconcileScaledObject(ctx context.Context, logg _, paused := scaledObject.GetAnnotations()[kedacontrollerutil.PausedReplicasAnnotation] if paused { logger.Info("ScaledObject is paused, so skipping the request.") + msg := kedav1alpha1.ScaledObjectConditionPausedMessage + conditions := scaledObject.Status.Conditions.DeepCopy() if err := r.stopScaleLoop(ctx, logger, scaledObject); err != nil { - return "failed to stop the scale loop for paused ScaledObject", err + msg = "failed to stop the scale loop for paused ScaledObject" + conditions.SetPausedCondition(metav1.ConditionFalse, "ScaledObjectStopScaleLoopFailed", msg) + return msg, err } if deleted, err := r.ensureHPAForScaledObjectIsDeleted(ctx, logger, scaledObject); !deleted { - return "failed to delete HPA for paused ScaledObject", err + msg = "failed to delete HPA for paused ScaledObject" + conditions.SetPausedCondition(metav1.ConditionFalse, "ScaledObjectHPADeleteFailed", msg) + return msg, err } - return kedav1alpha1.ScaledObjectConditionPausedMessage, nil + conditions.SetPausedCondition(metav1.ConditionTrue, kedav1alpha1.ScaledObjectConditionPausedReason, msg) + return msg, nil } // Check scale target Name is specified diff --git a/controllers/keda/scaledobject_controller_test.go b/controllers/keda/scaledobject_controller_test.go index 71edb8dccd7..996b454cb18 100644 --- a/controllers/keda/scaledobject_controller_test.go +++ b/controllers/keda/scaledobject_controller_test.go @@ -903,6 +903,15 @@ var _ = Describe("ScaledObjectController", func() { Eventually(func() error { return k8sClient.Get(context.Background(), types.NamespacedName{Name: fmt.Sprintf("keda-hpa-%s", soName), Namespace: "default"}, hpa) }).Should(HaveOccurred()) + + // wait so's Paused condition true + Eventually(func() metav1.ConditionStatus { + err := k8sClient.Get(context.Background(), types.NamespacedName{Name: soName, Namespace: "default"}, so) + if err != nil { + return metav1.ConditionUnknown + } + return so.Status.Conditions.GetPausedCondition().Status + }, 1*time.Minute).Should(Equal(metav1.ConditionTrue)) }) })