Skip to content

Commit 2ee13cb

Browse files
Use Installing/Upgrading reasons for active operations
Show Installing when first install is rolling out and Upgrading when an existing bundle is being updated. This gives users clearer status about what's happening instead of the vague Absent reason. Add e2e tests for Installing and Upgrading status reasons - Add test scenario for Installing reason during initial installation - Add test scenario for Upgrading reason during bundle upgrades - Implement ClusterExtensionEventuallyReportsConditionOrProgressed step - Handle transient states that may transition quickly in e2e tests Assisted-by: Cursors/Claude
1 parent e224f78 commit 2ee13cb

7 files changed

Lines changed: 442 additions & 19 deletions

File tree

api/v1/common_types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ const (
2121
TypeProgressing = "Progressing"
2222

2323
// Installed reasons
24-
ReasonAbsent = "Absent"
24+
ReasonAbsent = "Absent"
25+
ReasonInstalling = "Installing"
26+
ReasonUpgrading = "Upgrading"
2527

2628
// Progressing reasons
2729
ReasonRollingOut = "RollingOut"

internal/operator-controller/conditionsets/conditionsets.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ var ConditionReasons = []string{
4343
ocv1.ReasonInvalidConfiguration,
4444
ocv1.ReasonRetrying,
4545
ocv1.ReasonAbsent,
46+
ocv1.ReasonInstalling,
47+
ocv1.ReasonUpgrading,
4648
ocv1.ReasonRollingOut,
4749
ocv1.ReasonProgressDeadlineExceeded,
4850
}

internal/operator-controller/controllers/common_controller.go

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,38 +57,56 @@ func setInstalledStatusFromRevisionStates(ext *ocv1.ClusterExtension, revisionSt
5757
// Nothing is installed
5858
if revisionStates.Installed == nil {
5959
setInstallStatus(ext, nil)
60-
reason := determineFailureReason(revisionStates.RollingOut)
60+
reason := determineInstalledReason(revisionStates.RollingOut)
6161
setInstalledStatusConditionFalse(ext, reason, "No bundle installed")
6262
return
6363
}
64-
// Something is installed
64+
65+
// Something is installed - check if upgrade is in progress
6566
installStatus := &ocv1.ClusterExtensionInstallStatus{
6667
Bundle: revisionStates.Installed.BundleMetadata,
6768
}
6869
setInstallStatus(ext, installStatus)
70+
71+
if len(revisionStates.RollingOut) > 0 {
72+
latestRevision := revisionStates.RollingOut[len(revisionStates.RollingOut)-1]
73+
progressingCond := apimeta.FindStatusCondition(latestRevision.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
74+
75+
if progressingCond != nil && progressingCond.Reason == string(ocv1.ReasonRollingOut) {
76+
setInstalledStatusConditionUpgrading(ext, fmt.Sprintf("Upgrading from %s", revisionStates.Installed.Image))
77+
return
78+
}
79+
}
80+
6981
setInstalledStatusConditionSuccess(ext, fmt.Sprintf("Installed bundle %s successfully", revisionStates.Installed.Image))
7082
}
7183

72-
// determineFailureReason determines the appropriate reason for the Installed condition
84+
// determineInstalledReason determines the appropriate reason for the Installed condition
7385
// when no bundle is installed (Installed: False).
7486
//
7587
// Returns Failed when:
7688
// - No rolling revisions exist (nothing to install)
7789
// - The latest rolling revision has Reason: Retrying (indicates an error occurred)
7890
//
91+
// Returns Installing when:
92+
// - The latest rolling revision explicitly has Reason: RollingOut (healthy installation in progress)
93+
//
7994
// Returns Absent when:
80-
// - Rolling revisions exist with the latest having Reason: RollingOut (healthy phased rollout in progress)
95+
// - Rolling revisions exist but have no conditions set (rollout just started)
8196
//
8297
// Rationale:
8398
// - Failed: Semantically indicates an error prevented installation
84-
// - Absent: Semantically indicates "not there yet" (neutral state, e.g., during healthy rollout)
99+
// - Installing: Semantically indicates a first-time installation is actively in progress
100+
// - Absent: Neutral state when rollout exists but hasn't progressed enough to determine health
85101
// - Retrying reason indicates an error (config validation, apply failure, etc.)
86-
// - RollingOut reason indicates healthy progress (not an error)
102+
// - RollingOut reason indicates confirmed healthy progress
87103
// - Only the LATEST revision matters - old errors superseded by newer healthy revisions should not cause Failed
88104
//
105+
// Note: This function is only called when Installed == nil (first-time installation scenario).
89106
// Note: rollingRevisions are sorted in ascending order by Spec.Revision (oldest to newest),
90-
// so the latest revision is the LAST element in the array.
91-
func determineFailureReason(rollingRevisions []*RevisionMetadata) string {
107+
//
108+
// so the latest revision is the LAST element in the array.
109+
func determineInstalledReason(rollingRevisions []*RevisionMetadata) string {
92110
if len(rollingRevisions) == 0 {
93111
return ocv1.ReasonFailed
94112
}
@@ -97,14 +115,21 @@ func determineFailureReason(rollingRevisions []*RevisionMetadata) string {
97115
// Latest revision is the last element in the array (sorted ascending by Spec.Revision)
98116
latestRevision := rollingRevisions[len(rollingRevisions)-1]
99117
progressingCond := apimeta.FindStatusCondition(latestRevision.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
100-
if progressingCond != nil && progressingCond.Reason == string(ocv1.ClusterExtensionRevisionReasonRetrying) {
101-
// Retrying indicates an error occurred (config, apply, validation, etc.)
102-
// Use Failed for semantic correctness: installation failed due to error
103-
return ocv1.ReasonFailed
118+
if progressingCond != nil {
119+
if progressingCond.Reason == string(ocv1.ClusterExtensionRevisionReasonRetrying) {
120+
// Retrying indicates an error occurred (config, apply, validation, etc.)
121+
// Use Failed for semantic correctness: installation failed due to error
122+
return ocv1.ReasonFailed
123+
}
124+
if progressingCond.Reason == string(ocv1.ReasonRollingOut) {
125+
// RollingOut indicates healthy progress is confirmed
126+
// Use Installing to communicate that a first-time installation is actively in progress
127+
return ocv1.ReasonInstalling
128+
}
104129
}
105130

106-
// No error detected in latest revision - it's progressing healthily (RollingOut) or no conditions set
107-
// Use Absent for neutral "not installed yet" state
131+
// No progressing condition or unknown reason - rollout just started or hasn't progressed
132+
// Use Absent as neutral state
108133
return ocv1.ReasonAbsent
109134
}
110135

@@ -119,6 +144,17 @@ func setInstalledStatusConditionSuccess(ext *ocv1.ClusterExtension, message stri
119144
})
120145
}
121146

147+
// setInstalledStatusConditionUpgrading sets the installed status condition to upgrading.
148+
func setInstalledStatusConditionUpgrading(ext *ocv1.ClusterExtension, message string) {
149+
SetStatusCondition(&ext.Status.Conditions, metav1.Condition{
150+
Type: ocv1.TypeInstalled,
151+
Status: metav1.ConditionTrue,
152+
Reason: ocv1.ReasonUpgrading,
153+
Message: message,
154+
ObservedGeneration: ext.GetGeneration(),
155+
})
156+
}
157+
122158
// setInstalledStatusConditionFailed sets the installed status condition to failed.
123159
func setInstalledStatusConditionFalse(ext *ocv1.ClusterExtension, reason string, message string) {
124160
SetStatusCondition(&ext.Status.Conditions, metav1.Condition{

0 commit comments

Comments
 (0)