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

Operator is aware about changes on underlying ScaledObject #901

Merged
merged 6 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
JorTurFer marked this conversation as resolved.
Show resolved Hide resolved
- 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"
)
JorTurFer marked this conversation as resolved.
Show resolved Hide resolved

// 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{}
}
JorTurFer marked this conversation as resolved.
Show resolved Hide resolved
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{},
JorTurFer marked this conversation as resolved.
Show resolved Hide resolved
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)
}
Loading