From 4c211d6dc3ba08ae740c38b370d2805eec75eb7a Mon Sep 17 00:00:00 2001 From: Francesco Pantano Date: Fri, 5 Apr 2024 09:27:43 +0200 Subject: [PATCH] Re-init conditions each reconcile This patch follows the same pattern applied to the other operators, where we re-init the condition at each reconcile loop. Conditions are re-evaluated and updated, keeping the LastTransitionTime for those that haven't changed (it avoids the transition from True to Unknown to True again). In addition, the "observedGeneration" field is introduced, and it is used by the openstack-operator to check the IsReady() function for a particular CR in case a minor update is triggered. All the conditions are evaluated during the main Reconcile loop ( or the reconcileNormal function in some circumstances), hence the main ReadyCondition is updated within the same flow. The defer function still updates the Resource and Mirror the condition to the top-level CR. Signed-off-by: Francesco Pantano --- ...eee.openstack.org_openstackansibleees.yaml | 3 ++ api/v1beta1/openstack_ansibleee_types.go | 6 +++ ...eee.openstack.org_openstackansibleees.yaml | 3 ++ controllers/openstack_ansibleee_controller.go | 51 +++++++++++-------- docs/assemblies/openstack_ansibleee.adoc | 5 ++ 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/api/bases/ansibleee.openstack.org_openstackansibleees.yaml b/api/bases/ansibleee.openstack.org_openstackansibleees.yaml index 024e5415..48c69ed2 100644 --- a/api/bases/ansibleee.openstack.org_openstackansibleees.yaml +++ b/api/bases/ansibleee.openstack.org_openstackansibleees.yaml @@ -1585,6 +1585,9 @@ spec: type: string type: array type: object + observedGeneration: + format: int64 + type: integer type: object type: object served: true diff --git a/api/v1beta1/openstack_ansibleee_types.go b/api/v1beta1/openstack_ansibleee_types.go index 1d3fab2d..42ddfb82 100644 --- a/api/v1beta1/openstack_ansibleee_types.go +++ b/api/v1beta1/openstack_ansibleee_types.go @@ -134,6 +134,12 @@ type OpenStackAnsibleEEStatus struct { // +kubebuilder:default:=Pending // JobStatus status of the executed job (Pending/Running/Succeeded/Failed) JobStatus string `json:"JobStatus,omitempty" optional:"true"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the opentack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/ansibleee.openstack.org_openstackansibleees.yaml b/config/crd/bases/ansibleee.openstack.org_openstackansibleees.yaml index 024e5415..48c69ed2 100644 --- a/config/crd/bases/ansibleee.openstack.org_openstackansibleees.yaml +++ b/config/crd/bases/ansibleee.openstack.org_openstackansibleees.yaml @@ -1585,6 +1585,9 @@ spec: type: string type: array type: object + observedGeneration: + format: int64 + type: integer type: object type: object served: true diff --git a/controllers/openstack_ansibleee_controller.go b/controllers/openstack_ansibleee_controller.go index 6137c121..d26ed991 100644 --- a/controllers/openstack_ansibleee_controller.go +++ b/controllers/openstack_ansibleee_controller.go @@ -102,43 +102,44 @@ func (r *OpenStackAnsibleEEReconciler) Reconcile(ctx context.Context, req ctrl.R Log.Error(err, fmt.Sprintf("Unable to acquire helper for OpenStackAnsibleEE %s", instance.Name)) return ctrl.Result{}, err } + // Bump the ObservedGeneration as the new Reconciliation started + instance.Status.ObservedGeneration = instance.Generation + + // initialize status if Conditions is nil, but do not reset if it already + // exists + isNewInstance := instance.Status.Conditions == nil + if isNewInstance { + instance.Status.Conditions = condition.Conditions{} + } + + // Save a copy of the condtions so that we can restore the LastTransitionTime + // when a condition's state doesn't change. + savedConditions := instance.Status.Conditions.DeepCopy() // Always patch the instance status when exiting this function so we can // persist any changes. defer func() { - // update the overall status condition if service is ready - if instance.IsReady() { - instance.Status.Conditions.MarkTrue(condition.ReadyCondition, ansibleeev1.AnsibleExecutionJobReadyMessage) - } else { - // something is not ready so reset the Ready condition - instance.Status.Conditions.MarkUnknown( - condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage) - // and recalculate it based on the state of the rest of the conditions - instance.Status.Conditions.Set(instance.Status.Conditions.Mirror(condition.ReadyCondition)) + condition.RestoreLastTransitionTimes( + &instance.Status.Conditions, savedConditions) + if instance.Status.Conditions.IsUnknown(condition.ReadyCondition) { + instance.Status.Conditions.Set( + instance.Status.Conditions.Mirror(condition.ReadyCondition)) } - err := helper.PatchInstance(ctx, instance) if err != nil { - Log.Error(_err, "PatchInstance error") _err = err return } }() // Initialize Status - if instance.Status.Conditions == nil { - instance.Status.Conditions = condition.Conditions{} - cl := condition.CreateList( - condition.UnknownCondition(ansibleeev1.AnsibleExecutionJobReadyCondition, condition.InitReason, ansibleeev1.AnsibleExecutionJobInitMessage), - ) + cl := condition.CreateList( + condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage), + condition.UnknownCondition(ansibleeev1.AnsibleExecutionJobReadyCondition, condition.InitReason, ansibleeev1.AnsibleExecutionJobInitMessage), + ) - instance.Status.Conditions.Init(&cl) - - // Register overall status immediately to have an early feedback e.g. - // in the cli - return ctrl.Result{}, nil - } + instance.Status.Conditions.Init(&cl) // Initialize Status fields util.InitMap(&instance.Status.Hash) @@ -234,6 +235,12 @@ func (r *OpenStackAnsibleEEReconciler) Reconcile(ctx context.Context, req ctrl.R instance.Status.Conditions.MarkTrue(ansibleeev1.AnsibleExecutionJobReadyCondition, ansibleeev1.AnsibleExecutionJobReadyMessage) instance.Status.JobStatus = ansibleeev1.JobStatusSucceeded + // We reached the end of the Reconcile, update the Ready condition based on + // the sub conditions + if instance.Status.Conditions.AllSubConditionIsTrue() { + instance.Status.Conditions.MarkTrue( + condition.ReadyCondition, condition.ReadyMessage) + } Log.Info(fmt.Sprintf("Reconciled AnsibleEE '%s' successfully", instance.Name)) return ctrl.Result{}, nil } diff --git a/docs/assemblies/openstack_ansibleee.adoc b/docs/assemblies/openstack_ansibleee.adoc index 74c55b2f..4c4aaa39 100644 --- a/docs/assemblies/openstack_ansibleee.adoc +++ b/docs/assemblies/openstack_ansibleee.adoc @@ -212,6 +212,11 @@ OpenStackAnsibleEEStatus defines the observed state of OpenStackAnsibleEE | JobStatus status of the executed job (Pending/Running/Succeeded/Failed) | string | false + +| observedGeneration +| ObservedGeneration - the most recent generation observed for this service. If the observed generation is less than the spec generation, then the controller has not processed the latest changes injected by the opentack-operator in the top-level CR (e.g. the ContainerImage) +| int64 +| false |=== <>