Skip to content

Commit

Permalink
Operator is aware about changes on underlying ScaledObject (#901)
Browse files Browse the repository at this point in the history
* Operator is aware about changes on underlying ScaledObject

Signed-off-by: Jorge Turrado <[email protected]>

* .

Signed-off-by: Jorge Turrado <[email protected]>

* .

Signed-off-by: Jorge Turrado <[email protected]>

* Update CHANGELOG.md

Co-authored-by: Tom Kerkhove <[email protected]>
Signed-off-by: Jorge Turrado Ferrero <[email protected]>

---------

Signed-off-by: Jorge Turrado <[email protected]>
Signed-off-by: Jorge Turrado Ferrero <[email protected]>
Co-authored-by: Tom Kerkhove <[email protected]>
  • Loading branch information
JorTurFer and tomkerkhove authored Apr 2, 2024
1 parent dbac89d commit e6896c2
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 72 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ This changelog keeps track of work items that have been completed and are ready

### Fixes

- **General**: TODO ([#TODO](https://github.com/kedacore/http-add-on/issues/TODO))
- **General**: Ensure operator is aware about changes on underlying ScaledObject ([#900](https://github.com/kedacore/http-add-on/issues/900))

### Deprecations

Expand Down
6 changes: 0 additions & 6 deletions config/crd/bases/http.keda.sh_httpscaledobjects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,6 @@ spec:
type:
description: Type of condition
enum:
- Created
- Terminated
- Error
- Pending
- Terminating
- Unknown
- Ready
type: string
required:
Expand Down
88 changes: 88 additions & 0 deletions operator/apis/http/v1alpha1/condition_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// +kubebuilder:validation:Enum=Ready

// HTTPScaledObjectCreationStatus describes the creation status
// of the scaler's additional resources such as Services, Ingresses and Deployments
type HTTPScaledObjectCreationStatus string

const (
// Ready indicates the object is fully created
Ready HTTPScaledObjectCreationStatus = "Ready"
)

// +kubebuilder:validation:Enum=ErrorCreatingAppScaledObject;AppScaledObjectCreated;TerminatingResources;AppScaledObjectTerminated;AppScaledObjectTerminationError;PendingCreation;HTTPScaledObjectIsReady;

// HTTPScaledObjectConditionReason describes the reason why the condition transitioned
type HTTPScaledObjectConditionReason string

const (
ErrorCreatingAppScaledObject HTTPScaledObjectConditionReason = "ErrorCreatingAppScaledObject"
AppScaledObjectCreated HTTPScaledObjectConditionReason = "AppScaledObjectCreated"
TerminatingResources HTTPScaledObjectConditionReason = "TerminatingResources"
AppScaledObjectTerminated HTTPScaledObjectConditionReason = "AppScaledObjectTerminated"
AppScaledObjectTerminationError HTTPScaledObjectConditionReason = "AppScaledObjectTerminationError"
PendingCreation HTTPScaledObjectConditionReason = "PendingCreation"
HTTPScaledObjectIsReady HTTPScaledObjectConditionReason = "HTTPScaledObjectIsReady"
)

// HTTPScaledObjectCondition stores the condition state
type HTTPScaledObjectCondition struct {
// Timestamp of the condition
// +optional
Timestamp string `json:"timestamp" description:"Timestamp of this condition"`
// Type of condition
// +required
Type HTTPScaledObjectCreationStatus `json:"type" description:"type of status condition"`
// Status of the condition, one of True, False, Unknown.
// +required
Status metav1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"`
// Reason for the condition's last transition.
// +optional
Reason HTTPScaledObjectConditionReason `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"`
// Message indicating details about the transition.
// +optional
Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"`
}

type Conditions []HTTPScaledObjectCondition

// GetReadyCondition returns Condition of type Ready
func (c *Conditions) GetReadyCondition() HTTPScaledObjectCondition {
if *c == nil {
c = GetInitializedConditions()
}
return c.getCondition(Ready)
}

// GetInitializedConditions returns Conditions initialized to the default -> Status: Unknown
func GetInitializedConditions() *Conditions {
return &Conditions{{Type: Ready, Status: metav1.ConditionUnknown}}
}

// IsTrue is true if the condition is True
func (c *HTTPScaledObjectCondition) IsTrue() bool {
if c == nil {
return false
}
return c.Status == metav1.ConditionTrue
}

// IsFalse is true if the condition is False
func (c *HTTPScaledObjectCondition) IsFalse() bool {
if c == nil {
return false
}
return c.Status == metav1.ConditionFalse
}

func (c Conditions) getCondition(conditionType HTTPScaledObjectCreationStatus) HTTPScaledObjectCondition {
for i := range c {
if c[i].Type == conditionType {
return c[i]
}
}
return HTTPScaledObjectCondition{}
}
60 changes: 1 addition & 59 deletions operator/apis/http/v1alpha1/httpscaledobject_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,64 +74,6 @@ type HTTPScaledObjectSpec struct {
CooldownPeriod *int32 `json:"scaledownPeriod,omitempty" description:"Cooldown period (seconds) for resources to scale down (Default 300)"`
}

// +kubebuilder:validation:Enum=Created;Terminated;Error;Pending;Terminating;Unknown;Ready

// HTTPScaledObjectCreationStatus describes the creation status
// of the scaler's additional resources such as Services, Ingresses and Deployments
type HTTPScaledObjectCreationStatus string

const (
// Created indicates the resource has been created
Created HTTPScaledObjectCreationStatus = "Created"
// Terminated indicates the resource has been terminated
Terminated HTTPScaledObjectCreationStatus = "Terminated"
// Error indicates the resource had an error
Error HTTPScaledObjectCreationStatus = "Error"
// Pending indicates the resource hasn't been created
Pending HTTPScaledObjectCreationStatus = "Pending"
// Terminating indicates that the resource is marked for deletion but hasn't
// been deleted yet
Terminating HTTPScaledObjectCreationStatus = "Terminating"
// Unknown indicates the status is unavailable
Unknown HTTPScaledObjectCreationStatus = "Unknown"
// Ready indicates the object is fully created
Ready HTTPScaledObjectCreationStatus = "Ready"
)

// +kubebuilder:validation:Enum=ErrorCreatingAppScaledObject;AppScaledObjectCreated;TerminatingResources;AppScaledObjectTerminated;AppScaledObjectTerminationError;PendingCreation;HTTPScaledObjectIsReady;

// HTTPScaledObjectConditionReason describes the reason why the condition transitioned
type HTTPScaledObjectConditionReason string

const (
ErrorCreatingAppScaledObject HTTPScaledObjectConditionReason = "ErrorCreatingAppScaledObject"
AppScaledObjectCreated HTTPScaledObjectConditionReason = "AppScaledObjectCreated"
TerminatingResources HTTPScaledObjectConditionReason = "TerminatingResources"
AppScaledObjectTerminated HTTPScaledObjectConditionReason = "AppScaledObjectTerminated"
AppScaledObjectTerminationError HTTPScaledObjectConditionReason = "AppScaledObjectTerminationError"
PendingCreation HTTPScaledObjectConditionReason = "PendingCreation"
HTTPScaledObjectIsReady HTTPScaledObjectConditionReason = "HTTPScaledObjectIsReady"
)

// HTTPScaledObjectCondition stores the condition state
type HTTPScaledObjectCondition struct {
// Timestamp of the condition
// +optional
Timestamp string `json:"timestamp" description:"Timestamp of this condition"`
// Type of condition
// +required
Type HTTPScaledObjectCreationStatus `json:"type" description:"type of status condition"`
// Status of the condition, one of True, False, Unknown.
// +required
Status metav1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"`
// Reason for the condition's last transition.
// +optional
Reason HTTPScaledObjectConditionReason `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"`
// Message indicating details about the transition.
// +optional
Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"`
}

// HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
type HTTPScaledObjectStatus struct {
// TargetWorkload reflects details about the scaled workload.
Expand All @@ -141,7 +83,7 @@ type HTTPScaledObjectStatus struct {
// +optional
TargetService string `json:"targetService,omitempty" description:"It reflects details about the scaled service"`
// Conditions of the operator
Conditions []HTTPScaledObjectCondition `json:"conditions,omitempty" description:"List of auditable conditions of the operator"`
Conditions Conditions `json:"conditions,omitempty" description:"List of auditable conditions of the operator"`
}

// +genclient
Expand Down
21 changes: 20 additions & 1 deletion operator/apis/http/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion operator/controllers/http/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (r *HTTPScaledObjectReconciler) createOrUpdateApplicationResources(
httpso,
*SetMessage(
CreateCondition(
v1alpha1.Pending,
v1alpha1.Ready,
v1.ConditionUnknown,
v1alpha1.PendingCreation,
),
Expand Down
17 changes: 16 additions & 1 deletion operator/controllers/http/httpscaledobject_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"time"

kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -34,6 +35,7 @@ import (

httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
"github.com/kedacore/http-add-on/operator/controllers/http/config"
"github.com/kedacore/http-add-on/operator/controllers/util"
"github.com/kedacore/http-add-on/pkg/k8s"
)

Expand Down Expand Up @@ -146,7 +148,20 @@ func (r *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Req
// SetupWithManager sets up the controller with the Manager.
func (r *HTTPScaledObjectReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&httpv1alpha1.HTTPScaledObject{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
For(&httpv1alpha1.HTTPScaledObject{}, builder.WithPredicates(
predicate.Or(
predicate.GenerationChangedPredicate{},
util.HTTPScaledObjectReadyConditionPredicate{},
),
)).
// Trigger a reconcile only when the ScaledObject spec,label or annotation changes.
// Ignore updates to ScaledObject status
Owns(&kedav1alpha1.ScaledObject{}, builder.WithPredicates(
predicate.Or(
predicate.LabelChangedPredicate{},
predicate.AnnotationChangedPredicate{},
util.ScaledObjectSpecChangedPredicate{},
))).
Complete(r)
}

Expand Down
4 changes: 2 additions & 2 deletions operator/controllers/http/scaled_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (r *HTTPScaledObjectReconciler) createOrUpdateScaledObject(
httpso,
*SetMessage(
CreateCondition(
httpv1alpha1.Error,
httpv1alpha1.Ready,
v1.ConditionFalse,
httpv1alpha1.ErrorCreatingAppScaledObject,
),
Expand All @@ -97,7 +97,7 @@ func (r *HTTPScaledObjectReconciler) createOrUpdateScaledObject(
httpso,
*SetMessage(
CreateCondition(
httpv1alpha1.Created,
httpv1alpha1.Ready,
v1.ConditionTrue,
httpv1alpha1.AppScaledObjectCreated,
),
Expand Down
2 changes: 1 addition & 1 deletion operator/controllers/http/scaled_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestCreateOrUpdateScaledObject(t *testing.T) {
cond1ts, err := time.Parse(time.RFC3339, cond1.Timestamp)
r.NoError(err)
r.GreaterOrEqual(time.Since(cond1ts), time.Duration(0))
r.Equal(v1alpha1.Created, cond1.Type)
r.Equal(v1alpha1.Ready, cond1.Type)
r.Equal(metav1.ConditionTrue, cond1.Status)
r.Equal(v1alpha1.AppScaledObjectCreated, cond1.Reason)

Expand Down
52 changes: 52 additions & 0 deletions operator/controllers/util/predicate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package util

import (
kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"k8s.io/apimachinery/pkg/api/equality"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"

"github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
)

type HTTPScaledObjectReadyConditionPredicate struct {
predicate.Funcs
}

func (HTTPScaledObjectReadyConditionPredicate) Update(e event.UpdateEvent) bool {
if e.ObjectOld == nil || e.ObjectNew == nil {
return false
}

var newReadyCondition, oldReadyCondition v1alpha1.HTTPScaledObjectCondition

oldObj, ok := e.ObjectOld.(*v1alpha1.HTTPScaledObject)
if !ok {
return false
}
oldReadyCondition = oldObj.Status.Conditions.GetReadyCondition()

newObj, ok := e.ObjectNew.(*v1alpha1.HTTPScaledObject)
if !ok {
return false
}
newReadyCondition = newObj.Status.Conditions.GetReadyCondition()

// False/Unknown -> True
if !oldReadyCondition.IsTrue() && newReadyCondition.IsTrue() {
return true
}

return false
}

type ScaledObjectSpecChangedPredicate struct {
predicate.Funcs
}

func (ScaledObjectSpecChangedPredicate) Update(e event.UpdateEvent) bool {
newObj := e.ObjectNew.(*kedav1alpha1.ScaledObject)
oldObj := e.ObjectOld.(*kedav1alpha1.ScaledObject)

return !equality.Semantic.DeepDerivative(newObj.Spec, oldObj.Spec)
}

0 comments on commit e6896c2

Please sign in to comment.