@@ -111,3 +111,179 @@ func TestGetClusterVersion(t *testing.T) {
111111 })
112112 }
113113}
114+
115+ func TestIsClusterUpgrading (t * testing.T ) {
116+ for _ , tt := range []struct {
117+ name string
118+ mocks func (* configv1.ClusterVersion )
119+ nilCV bool
120+ want bool
121+ comment string
122+ }{
123+ {
124+ name : "nil ClusterVersion returns false" ,
125+ nilCV : true ,
126+ want : false ,
127+ comment : "Safety check for nil input" ,
128+ },
129+ {
130+ name : "empty history returns false" ,
131+ mocks : nil ,
132+ want : false ,
133+ comment : "Fresh cluster or no upgrade history" ,
134+ },
135+ {
136+ name : "steady state - Completed update, Progressing=False returns false" ,
137+ mocks : func (cv * configv1.ClusterVersion ) {
138+ cv .Status .History = []configv1.UpdateHistory {
139+ {
140+ State : configv1 .CompletedUpdate ,
141+ Version : "4.18.30" ,
142+ },
143+ }
144+ cv .Status .Conditions = []configv1.ClusterOperatorStatusCondition {
145+ {
146+ Type : configv1 .OperatorProgressing ,
147+ Status : configv1 .ConditionFalse ,
148+ },
149+ }
150+ },
151+ want : false ,
152+ comment : "Normal steady state cluster" ,
153+ },
154+ {
155+ name : "ARO-26990 bug case - Completed update but Progressing=True returns false" ,
156+ mocks : func (cv * configv1.ClusterVersion ) {
157+ cv .Status .History = []configv1.UpdateHistory {
158+ {
159+ State : configv1 .CompletedUpdate ,
160+ Version : "4.18.30" ,
161+ },
162+ }
163+ cv .Status .Conditions = []configv1.ClusterOperatorStatusCondition {
164+ {
165+ Type : configv1 .OperatorProgressing ,
166+ Status : configv1 .ConditionTrue ,
167+ Reason : "ClusterOperatorProgressing" ,
168+ Message : "Working towards 4.18.30: some cluster operators are still updating" ,
169+ },
170+ }
171+ },
172+ want : false ,
173+ comment : "MCO rollout from ARO MC deploy - should NOT trigger dnsmasq updates" ,
174+ },
175+ {
176+ name : "upgrade intent not initiated - desiredUpdate set but history[0] still Completed returns false" ,
177+ mocks : func (cv * configv1.ClusterVersion ) {
178+ cv .Spec .DesiredUpdate = & configv1.Update {
179+ Version : "4.19.0" ,
180+ }
181+ cv .Status .History = []configv1.UpdateHistory {
182+ {
183+ State : configv1 .CompletedUpdate ,
184+ Version : "4.18.30" ,
185+ },
186+ }
187+ cv .Status .Conditions = []configv1.ClusterOperatorStatusCondition {
188+ {
189+ Type : configv1 .OperatorProgressing ,
190+ Status : configv1 .ConditionFalse ,
191+ },
192+ }
193+ },
194+ want : false ,
195+ comment : "Customer expressed upgrade intent but CVO hasn't started (e.g., pending adminack)" ,
196+ },
197+ {
198+ name : "upgrade initiated - Partial update at history[0] returns true" ,
199+ mocks : func (cv * configv1.ClusterVersion ) {
200+ cv .Spec .DesiredUpdate = & configv1.Update {
201+ Version : "4.19.0" ,
202+ }
203+ cv .Status .History = []configv1.UpdateHistory {
204+ {
205+ State : configv1 .PartialUpdate ,
206+ Version : "4.19.0" ,
207+ },
208+ {
209+ State : configv1 .CompletedUpdate ,
210+ Version : "4.18.30" ,
211+ },
212+ }
213+ cv .Status .Conditions = []configv1.ClusterOperatorStatusCondition {
214+ {
215+ Type : configv1 .OperatorProgressing ,
216+ Status : configv1 .ConditionTrue ,
217+ Reason : "ClusterOperatorProgressing" ,
218+ Message : "Working towards 4.19.0" ,
219+ },
220+ }
221+ },
222+ want : true ,
223+ comment : "OCP upgrade in progress - dnsmasq updates allowed" ,
224+ },
225+ {
226+ name : "upgrade initiated - no State field (treated as non-Completed) returns true" ,
227+ mocks : func (cv * configv1.ClusterVersion ) {
228+ cv .Status .History = []configv1.UpdateHistory {
229+ {
230+ Version : "4.19.0" ,
231+ // State field missing - older clusters or edge cases
232+ },
233+ {
234+ State : configv1 .CompletedUpdate ,
235+ Version : "4.18.30" ,
236+ },
237+ }
238+ },
239+ want : true ,
240+ comment : "Missing State field treated as upgrade in progress" ,
241+ },
242+ {
243+ name : "multiple completed updates returns false" ,
244+ mocks : func (cv * configv1.ClusterVersion ) {
245+ cv .Status .History = []configv1.UpdateHistory {
246+ {
247+ State : configv1 .CompletedUpdate ,
248+ Version : "4.18.30" ,
249+ },
250+ {
251+ State : configv1 .CompletedUpdate ,
252+ Version : "4.18.0" ,
253+ },
254+ {
255+ State : configv1 .CompletedUpdate ,
256+ Version : "4.17.38" ,
257+ },
258+ }
259+ },
260+ want : false ,
261+ comment : "Cluster with upgrade history, currently at steady state" ,
262+ },
263+ } {
264+ t .Run (tt .name , func (t * testing.T ) {
265+ var cv * configv1.ClusterVersion
266+
267+ if ! tt .nilCV {
268+ cv = & configv1.ClusterVersion {
269+ ObjectMeta : metav1.ObjectMeta {
270+ Name : "version" ,
271+ },
272+ Status : configv1.ClusterVersionStatus {
273+ History : []configv1.UpdateHistory {},
274+ Conditions : []configv1.ClusterOperatorStatusCondition {},
275+ },
276+ }
277+
278+ if tt .mocks != nil {
279+ tt .mocks (cv )
280+ }
281+ }
282+
283+ got := IsClusterUpgrading (cv )
284+ if got != tt .want {
285+ t .Errorf ("IsClusterUpgrading() = %v, want %v (%s)" , got , tt .want , tt .comment )
286+ }
287+ })
288+ }
289+ }
0 commit comments