@@ -2,6 +2,7 @@ package network
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "time"
78
@@ -203,7 +204,13 @@ func (builder *OperatorBuilder) SetIPForwarding(
203204 return builder , err
204205}
205206
206- // SetMultiNetworkPolicy enables network.operator multinetworkpolicy feature.
207+ // SetMultiNetworkPolicy enables or disables the network.operator multinetworkpolicy feature.
208+ // When disabling, the Cluster Network Operator often does not set Progressing=True; the initial
209+ // wait for Progressing=True is skipped in that case only. Enabling keeps the original sequence.
210+ // On disable, we wait until status.observedGeneration catches metadata.generation from the update
211+ // so condition polling is not satisfied from stale status from before this change.
212+ // The wait for Progressing=False uses a disable-specific path: CNO may omit the Progressing
213+ // condition when idle, which would otherwise never match WaitUntilInCondition(..., False).
207214func (builder * OperatorBuilder ) SetMultiNetworkPolicy (state bool , timeout time.Duration ) (* OperatorBuilder , error ) {
208215 if valid , err := builder .validate (); ! valid {
209216 return builder , err
@@ -221,20 +228,34 @@ func (builder *OperatorBuilder) SetMultiNetworkPolicy(state bool, timeout time.D
221228 return nil , err
222229 }
223230
224- err = builder .WaitUntilInCondition (
225- operatorv1 .OperatorStatusTypeProgressing , 60 * time .Second , operatorv1 .ConditionTrue )
226- if err != nil {
227- return nil , err
231+ targetGeneration := builder .Definition .Generation
232+
233+ if state {
234+ err = builder .WaitUntilInCondition (
235+ operatorv1 .OperatorStatusTypeProgressing , 60 * time .Second , operatorv1 .ConditionTrue )
236+ if err != nil {
237+ return nil , err
238+ }
239+ } else {
240+ err = builder .waitUntilObservedGeneration (targetGeneration , timeout )
241+ if err != nil {
242+ return nil , err
243+ }
244+ }
245+
246+ if state {
247+ err = builder .WaitUntilInCondition (
248+ operatorv1 .OperatorStatusTypeProgressing , timeout , operatorv1 .ConditionFalse )
249+ } else {
250+ err = builder .waitUntilProgressingSettledOnDisable (timeout )
228251 }
229252
230- err = builder .WaitUntilInCondition (
231- operatorv1 .OperatorStatusTypeProgressing , timeout , operatorv1 .ConditionFalse )
232253 if err != nil {
233254 return nil , err
234255 }
235256
236257 return builder , builder .WaitUntilInCondition (
237- operatorv1 .OperatorStatusTypeAvailable , 60 * time . Second , operatorv1 .ConditionTrue )
258+ operatorv1 .OperatorStatusTypeAvailable , timeout , operatorv1 .ConditionTrue )
238259 }
239260
240261 return builder , err
@@ -288,8 +309,8 @@ func (builder *OperatorBuilder) WaitUntilInCondition(
288309 return err
289310 }
290311
291- klog .V (100 ).Infof ("Wait until network.operator object %s is in condition %v " ,
292- builder .Definition .Name , condition )
312+ klog .V (100 ).Infof ("Wait until network.operator object %s has condition %s=%s " ,
313+ builder .Definition .Name , condition , status )
293314
294315 err := wait .PollUntilContextTimeout (
295316 context .TODO (), 3 * time .Second , timeout , true , func (ctx context.Context ) (bool , error ) {
@@ -306,9 +327,85 @@ func (builder *OperatorBuilder) WaitUntilInCondition(
306327 return false , nil
307328 })
308329
330+ if err != nil && errors .Is (err , context .DeadlineExceeded ) && builder .Exists () {
331+ klog .V (100 ).Infof ("timeout waiting for network.operator %s condition %s=%s; last status conditions: %#v" ,
332+ builder .Definition .Name , condition , status , builder .Object .Status .Conditions )
333+ }
334+
309335 return err
310336}
311337
338+ // waitUntilObservedGeneration waits until status.observedGeneration reflects the given metadata.generation,
339+ // indicating the operator has reconciled that spec revision.
340+ func (builder * OperatorBuilder ) waitUntilObservedGeneration (targetGeneration int64 , timeout time.Duration ) error {
341+ if valid , err := builder .validate (); ! valid {
342+ return err
343+ }
344+
345+ klog .V (100 ).Infof ("Wait until network.operator %s observedGeneration >= %d" ,
346+ builder .Definition .Name , targetGeneration )
347+
348+ return wait .PollUntilContextTimeout (
349+ context .TODO (), 3 * time .Second , timeout , true , func (ctx context.Context ) (bool , error ) {
350+ if ! builder .Exists () {
351+ return false , fmt .Errorf ("network.operator object %s does not exist" , builder .Definition .Name )
352+ }
353+
354+ return builder .Object .Status .ObservedGeneration >= targetGeneration , nil
355+ })
356+ }
357+
358+ // waitUntilProgressingSettledOnDisable waits until Progressing is False, or until the operator
359+ // reports available and not degraded while Progressing is absent (CNO may omit Progressing when idle).
360+ func (builder * OperatorBuilder ) waitUntilProgressingSettledOnDisable (timeout time.Duration ) error {
361+ if valid , err := builder .validate (); ! valid {
362+ return err
363+ }
364+
365+ klog .V (100 ).Infof ("Wait until network.operator %s is settled after disabling MultiNetworkPolicy" ,
366+ builder .Definition .Name )
367+
368+ return wait .PollUntilContextTimeout (
369+ context .TODO (), 3 * time .Second , timeout , true , func (ctx context.Context ) (bool , error ) {
370+ if ! builder .Exists () {
371+ return false , fmt .Errorf ("network.operator object %s does not exist" , builder .Definition .Name )
372+ }
373+
374+ for _ , c := range builder .Object .Status .Conditions {
375+ if c .Type != operatorv1 .OperatorStatusTypeProgressing {
376+ continue
377+ }
378+
379+ return c .Status == operatorv1 .ConditionFalse , nil
380+ }
381+
382+ return operatorAvailableAndNotDegraded (builder .Object .Status .Conditions ), nil
383+ })
384+ }
385+
386+ func operatorAvailableAndNotDegraded (conditions []operatorv1.OperatorCondition ) bool {
387+ var available , degraded * operatorv1.OperatorCondition
388+
389+ for i := range conditions {
390+ switch conditions [i ].Type {
391+ case operatorv1 .OperatorStatusTypeAvailable :
392+ available = & conditions [i ]
393+ case operatorv1 .OperatorStatusTypeDegraded :
394+ degraded = & conditions [i ]
395+ }
396+ }
397+
398+ if available == nil || available .Status != operatorv1 .ConditionTrue {
399+ return false
400+ }
401+
402+ if degraded != nil && degraded .Status == operatorv1 .ConditionTrue {
403+ return false
404+ }
405+
406+ return len (conditions ) > 0
407+ }
408+
312409// validate will check that the builder and builder definition are properly initialized before
313410// accessing any member fields.
314411func (builder * OperatorBuilder ) validate () (bool , error ) {
0 commit comments