mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-02-14 10:00:11 +00:00
fix: Check Applied condition before evaluating rollout status (#1243)
🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Qing Hao <qhao@redhat.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -218,15 +218,10 @@ func (d *deployReconciler) clusterRolloutStatusFunc(clusterName string, manifest
|
||||
// Get all relevant conditions
|
||||
progressingCond := apimeta.FindStatusCondition(manifestWork.Status.Conditions, workv1.WorkProgressing)
|
||||
degradedCond := apimeta.FindStatusCondition(manifestWork.Status.Conditions, workv1.WorkDegraded)
|
||||
appliedCond := apimeta.FindStatusCondition(manifestWork.Status.Conditions, workv1.WorkApplied)
|
||||
|
||||
// Return ToApply if:
|
||||
// - No Progressing condition exists yet (work hasn't been reconciled by agent)
|
||||
// - Progressing condition hasn't observed the latest spec
|
||||
// - Degraded condition exists but hasn't observed the latest spec
|
||||
// (Degraded is optional, but if it exists, we wait for it to catch up)
|
||||
if progressingCond == nil ||
|
||||
progressingCond.ObservedGeneration != manifestWork.Generation ||
|
||||
(degradedCond != nil && degradedCond.ObservedGeneration != manifestWork.Generation) {
|
||||
// Check if the work should be in ToApply status
|
||||
if shouldReturnToApply(manifestWork.Generation, appliedCond, progressingCond, degradedCond) {
|
||||
return clsRolloutStatus, nil
|
||||
}
|
||||
|
||||
@@ -262,6 +257,60 @@ func (d *deployReconciler) clusterRolloutStatusFunc(clusterName string, manifest
|
||||
return clsRolloutStatus, nil
|
||||
}
|
||||
|
||||
// shouldReturnToApply determines if the ManifestWork should be in ToApply status
|
||||
// based on the state of its conditions.
|
||||
//
|
||||
// Returns true if:
|
||||
// - The Applied condition is not ready (missing, hasn't observed latest spec, or not True)
|
||||
// - The Progressing condition is not ready (missing or hasn't observed latest spec)
|
||||
// - The Degraded condition exists but hasn't observed the latest spec
|
||||
//
|
||||
// IMPORTANT: Applied condition is checked FIRST to ensure the work has been properly applied
|
||||
// by the spoke agent before checking other agent-side conditions (Progressing/Degraded).
|
||||
// This prevents using stale timestamps from previous generations when conditions update
|
||||
// their ObservedGeneration without changing Status.
|
||||
func shouldReturnToApply(generation int64, appliedCond, progressingCond, degradedCond *metav1.Condition) bool {
|
||||
// Check Applied condition first - work must be applied by spoke agent
|
||||
if !isConditionReady(appliedCond, generation, true) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check Progressing condition - work must be reconciled by spoke agent
|
||||
if !isConditionReady(progressingCond, generation, false) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check Degraded condition if it exists - it must have observed the latest spec
|
||||
// Degraded is optional, but if it exists, we wait for it to catch up to avoid
|
||||
// using stale status information
|
||||
if degradedCond != nil && degradedCond.ObservedGeneration != generation {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isConditionReady checks if a condition is ready for rollout status evaluation.
|
||||
// A condition is ready if:
|
||||
// - It exists (not nil)
|
||||
// - It has observed the latest generation
|
||||
// - If requireTrue is set, it must also have Status=True
|
||||
func isConditionReady(cond *metav1.Condition, generation int64, requireTrue bool) bool {
|
||||
if cond == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if cond.ObservedGeneration != generation {
|
||||
return false
|
||||
}
|
||||
|
||||
if requireTrue && cond.Status != metav1.ConditionTrue {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// GetManifestworkApplied return only True status if there all clusters have manifests applied as expected
|
||||
func GetManifestworkApplied(reason string, message string) metav1.Condition {
|
||||
if reason == workapiv1alpha1.ReasonAsExpected {
|
||||
|
||||
@@ -591,6 +591,14 @@ func TestRequeueWithProgressDeadline(t *testing.T) {
|
||||
},
|
||||
}
|
||||
mw, _ := CreateManifestWork(mwrSet, "cls1", "place-test")
|
||||
// Set Applied=True first to ensure work has been applied by hub controller
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Applied",
|
||||
ObservedGeneration: mw.Generation,
|
||||
LastTransitionTime: metav1.NewTime(time.Now()),
|
||||
})
|
||||
// Set Progressing=True AND Degraded=True to simulate a failed work (matching new logic)
|
||||
apimeta.SetStatusCondition(&mw.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
@@ -850,7 +858,7 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
expectedLastTransition: nil,
|
||||
},
|
||||
{
|
||||
name: "degraded condition with unobserved generation - should return ToApply",
|
||||
name: "no Applied condition with stale Degraded - should return ToApply",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
@@ -891,6 +899,13 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
@@ -915,6 +930,13 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
@@ -935,68 +957,6 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
expectedStatus: clustersdkv1alpha1.Failed,
|
||||
expectedLastTransition: &now,
|
||||
},
|
||||
{
|
||||
name: "progressing true but degraded false - should return Progressing",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
Namespace: "cls1",
|
||||
Generation: 2,
|
||||
CreationTimestamp: creationTime,
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applying",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkDegraded,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Healthy",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: clustersdkv1alpha1.Progressing,
|
||||
expectedLastTransition: &now,
|
||||
},
|
||||
{
|
||||
name: "progressing true with degraded unknown - should return Progressing",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
Namespace: "cls1",
|
||||
Generation: 2,
|
||||
CreationTimestamp: creationTime,
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applying",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkDegraded,
|
||||
Status: metav1.ConditionUnknown,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Unknown",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: clustersdkv1alpha1.Progressing,
|
||||
expectedLastTransition: &now,
|
||||
},
|
||||
{
|
||||
name: "progressing false - should return Succeeded",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
@@ -1008,6 +968,13 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
@@ -1032,6 +999,13 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
@@ -1063,6 +1037,13 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionUnknown,
|
||||
@@ -1076,6 +1057,130 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
expectedStatus: clustersdkv1alpha1.Progressing,
|
||||
expectedLastTransition: &now,
|
||||
},
|
||||
{
|
||||
name: "WorkApplied Status=False with current generation - should return ToApply",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
Namespace: "cls1",
|
||||
Generation: 2,
|
||||
CreationTimestamp: creationTime,
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "ApplyFailed",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Completed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: clustersdkv1alpha1.ToApply,
|
||||
expectedLastTransition: nil,
|
||||
},
|
||||
{
|
||||
name: "WorkApplied Status=Unknown with current generation - should return ToApply",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
Namespace: "cls1",
|
||||
Generation: 2,
|
||||
CreationTimestamp: creationTime,
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionUnknown,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "ApplyStatusUnknown",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Completed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: clustersdkv1alpha1.ToApply,
|
||||
expectedLastTransition: nil,
|
||||
},
|
||||
{
|
||||
name: "WorkApplied with stale ObservedGeneration (old gen 1, current gen 2) - should return ToApply",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
Namespace: "cls1",
|
||||
Generation: 2,
|
||||
CreationTimestamp: creationTime,
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Completed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: clustersdkv1alpha1.ToApply,
|
||||
expectedLastTransition: nil,
|
||||
},
|
||||
{
|
||||
name: "Progressing with old ObservedGeneration but newer WorkApplied - should return ToApply",
|
||||
manifestWork: &workapiv1.ManifestWork{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-mw",
|
||||
Namespace: "cls1",
|
||||
Generation: 3,
|
||||
CreationTimestamp: creationTime,
|
||||
},
|
||||
Status: workapiv1.ManifestWorkStatus{
|
||||
Conditions: []metav1.Condition{
|
||||
{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 3,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Applied",
|
||||
},
|
||||
{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 2,
|
||||
LastTransitionTime: now,
|
||||
Reason: "Completed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: clustersdkv1alpha1.ToApply,
|
||||
expectedLastTransition: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -1106,3 +1211,338 @@ func TestClusterRolloutStatusFunc(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldReturnToApply(t *testing.T) {
|
||||
now := metav1.Now()
|
||||
generation := int64(2)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
generation int64
|
||||
appliedCond *metav1.Condition
|
||||
progressCond *metav1.Condition
|
||||
degradedCond *metav1.Condition
|
||||
expected bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "applied condition is nil - should return true",
|
||||
generation: generation,
|
||||
appliedCond: nil,
|
||||
progressCond: &metav1.Condition{Type: workapiv1.WorkProgressing, Status: metav1.ConditionTrue, ObservedGeneration: generation},
|
||||
degradedCond: nil,
|
||||
expected: true,
|
||||
description: "When Applied condition doesn't exist, work hasn't been applied by hub controller yet",
|
||||
},
|
||||
{
|
||||
name: "applied condition has stale generation - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{Type: workapiv1.WorkProgressing, Status: metav1.ConditionTrue, ObservedGeneration: generation},
|
||||
degradedCond: nil,
|
||||
expected: true,
|
||||
description: "When Applied condition hasn't observed latest spec, work needs to be reapplied",
|
||||
},
|
||||
{
|
||||
name: "applied condition is false - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{Type: workapiv1.WorkProgressing, Status: metav1.ConditionTrue, ObservedGeneration: generation},
|
||||
degradedCond: nil,
|
||||
expected: true,
|
||||
description: "When Applied condition is False, work failed to apply",
|
||||
},
|
||||
{
|
||||
name: "progressing condition is nil - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: nil,
|
||||
degradedCond: nil,
|
||||
expected: true,
|
||||
description: "When Progressing condition doesn't exist, work hasn't been reconciled by agent yet",
|
||||
},
|
||||
{
|
||||
name: "progressing condition has stale generation - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: nil,
|
||||
expected: true,
|
||||
description: "When Progressing condition hasn't observed latest spec, agent hasn't reconciled the new work yet",
|
||||
},
|
||||
{
|
||||
name: "degraded condition exists with stale generation - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkDegraded,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
expected: true,
|
||||
description: "When Degraded condition exists but hasn't observed latest spec, we wait for it to catch up",
|
||||
},
|
||||
{
|
||||
name: "all conditions ready - should return false",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: nil,
|
||||
expected: false,
|
||||
description: "When all conditions are current, proceed with rollout status evaluation",
|
||||
},
|
||||
{
|
||||
name: "all conditions ready including degraded - should return false",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkDegraded,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
expected: false,
|
||||
description: "When all conditions including Degraded are current, proceed with rollout status evaluation",
|
||||
},
|
||||
{
|
||||
name: "applied condition status unknown - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionUnknown,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: nil,
|
||||
expected: true,
|
||||
description: "When Applied condition status is Unknown even with current generation, work is not ready to apply",
|
||||
},
|
||||
{
|
||||
name: "degraded ahead of applied generation - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkDegraded,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
expected: true,
|
||||
description: "When Degraded is ahead of Applied generation, wait for Applied to catch up",
|
||||
},
|
||||
{
|
||||
name: "applied and progressing both stale but degraded current - should return true",
|
||||
generation: generation,
|
||||
appliedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
progressCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
degradedCond: &metav1.Condition{
|
||||
Type: workapiv1.WorkDegraded,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
expected: true,
|
||||
description: "When both Applied and Progressing are stale, return ToApply regardless of Degraded status",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := shouldReturnToApply(tt.generation, tt.appliedCond, tt.progressCond, tt.degradedCond)
|
||||
assert.Equal(t, tt.expected, result, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsConditionReady(t *testing.T) {
|
||||
now := metav1.Now()
|
||||
generation := int64(2)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
condition *metav1.Condition
|
||||
generation int64
|
||||
requireTrue bool
|
||||
expected bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "nil condition - should return false",
|
||||
condition: nil,
|
||||
generation: generation,
|
||||
requireTrue: false,
|
||||
expected: false,
|
||||
description: "When condition doesn't exist, it's not ready",
|
||||
},
|
||||
{
|
||||
name: "stale observed generation - should return false",
|
||||
condition: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: 1,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
generation: generation,
|
||||
requireTrue: false,
|
||||
expected: false,
|
||||
description: "When condition hasn't observed latest generation, it's not ready",
|
||||
},
|
||||
{
|
||||
name: "current generation but status not true when required - should return false",
|
||||
condition: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
generation: generation,
|
||||
requireTrue: true,
|
||||
expected: false,
|
||||
description: "When requireTrue is set and status is False, condition is not ready",
|
||||
},
|
||||
{
|
||||
name: "current generation but status unknown when required true - should return false",
|
||||
condition: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionUnknown,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
generation: generation,
|
||||
requireTrue: true,
|
||||
expected: false,
|
||||
description: "When requireTrue is set and status is Unknown, condition is not ready",
|
||||
},
|
||||
{
|
||||
name: "current generation and status true when required - should return true",
|
||||
condition: &metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
generation: generation,
|
||||
requireTrue: true,
|
||||
expected: true,
|
||||
description: "When condition has current generation and status is True when required, it's ready",
|
||||
},
|
||||
{
|
||||
name: "current generation and status false when not requiring true - should return true",
|
||||
condition: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
generation: generation,
|
||||
requireTrue: false,
|
||||
expected: true,
|
||||
description: "When requireTrue is false, any status with current generation is ready",
|
||||
},
|
||||
{
|
||||
name: "current generation and status true when not requiring true - should return true",
|
||||
condition: &metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: generation,
|
||||
LastTransitionTime: now,
|
||||
},
|
||||
generation: generation,
|
||||
requireTrue: false,
|
||||
expected: true,
|
||||
description: "When requireTrue is false, any status with current generation is ready",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := isConditionReady(tt.condition, tt.generation, tt.requireTrue)
|
||||
assert.Equal(t, tt.expected, result, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +386,12 @@ var _ = ginkgo.Describe("ManifestWorkReplicaSet", func() {
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
for _, work := range works.Items {
|
||||
workCopy := work.DeepCopy()
|
||||
meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "AppliedManifestWorkComplete",
|
||||
ObservedGeneration: workCopy.Generation,
|
||||
})
|
||||
meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
@@ -404,6 +410,12 @@ var _ = ginkgo.Describe("ManifestWorkReplicaSet", func() {
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
for _, work := range works.Items {
|
||||
workCopy := work.DeepCopy()
|
||||
meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "AppliedManifestWorkComplete",
|
||||
ObservedGeneration: workCopy.Generation,
|
||||
})
|
||||
meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionFalse,
|
||||
@@ -441,6 +453,12 @@ var _ = ginkgo.Describe("ManifestWorkReplicaSet", func() {
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
for _, work := range works.Items {
|
||||
workCopy := work.DeepCopy()
|
||||
meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Applied",
|
||||
ObservedGeneration: workCopy.Generation,
|
||||
})
|
||||
meta.SetStatusCondition(&workCopy.Status.Conditions, metav1.Condition{
|
||||
Type: workapiv1.WorkProgressing,
|
||||
Status: metav1.ConditionTrue,
|
||||
@@ -497,6 +515,14 @@ var _ = ginkgo.Describe("ManifestWorkReplicaSet", func() {
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
for _, work := range works.Items {
|
||||
workCopy := work.DeepCopy()
|
||||
meta.SetStatusCondition(
|
||||
&workCopy.Status.Conditions,
|
||||
metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Applied",
|
||||
ObservedGeneration: workCopy.Generation,
|
||||
})
|
||||
meta.SetStatusCondition(
|
||||
&workCopy.Status.Conditions,
|
||||
metav1.Condition{
|
||||
@@ -564,6 +590,14 @@ var _ = ginkgo.Describe("ManifestWorkReplicaSet", func() {
|
||||
gomega.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
for _, work := range works.Items {
|
||||
workCopy := work.DeepCopy()
|
||||
meta.SetStatusCondition(
|
||||
&workCopy.Status.Conditions,
|
||||
metav1.Condition{
|
||||
Type: workapiv1.WorkApplied,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "Applied",
|
||||
ObservedGeneration: workCopy.Generation,
|
||||
})
|
||||
meta.SetStatusCondition(
|
||||
&workCopy.Status.Conditions,
|
||||
metav1.Condition{
|
||||
|
||||
Reference in New Issue
Block a user