@@ -142,6 +142,40 @@ func PatchBMHForProvisioning(ctx context.Context, input PatchBMHForProvisioningI
142142 return helper .Patch (ctx , input .bmh )
143143}
144144
145+ // WaitForBmhReconciled waits for the BMO controller to process a BMH update.
146+ // This is used after BMO deployment rollout to ensure the controller is actually
147+ // processing events before making further changes. It works by adding/updating
148+ // an annotation and waiting for the status.lastUpdated timestamp to change,
149+ // which proves the controller reconciled the BMH.
150+ func WaitForBmhReconciled (ctx context.Context , c client.Client , bmh metal3api.BareMetalHost , intervals ... interface {}) {
151+ // Get the current BMH state
152+ key := types.NamespacedName {Namespace : bmh .Namespace , Name : bmh .Name }
153+ currentBmh := & metal3api.BareMetalHost {}
154+ Expect (c .Get (ctx , key , currentBmh )).To (Succeed ())
155+ initialLastUpdated := currentBmh .Status .LastUpdated
156+
157+ // Touch the BMH with an annotation update to trigger a reconcile
158+ helper , err := patch .NewHelper (currentBmh , c )
159+ Expect (err ).NotTo (HaveOccurred ())
160+ if currentBmh .Annotations == nil {
161+ currentBmh .Annotations = make (map [string ]string )
162+ }
163+ currentBmh .Annotations ["e2e.metal3.io/reconcile-check" ] = metav1 .Now ().Format ("2006-01-02T15:04:05Z" )
164+ Expect (helper .Patch (ctx , currentBmh )).To (Succeed ())
165+
166+ // Wait for lastUpdated to change, proving the controller processed our update
167+ Eventually (func (g Gomega ) {
168+ updatedBmh := & metal3api.BareMetalHost {}
169+ g .Expect (c .Get (ctx , key , updatedBmh )).To (Succeed ())
170+ // Check that lastUpdated has changed (controller reconciled)
171+ g .Expect (updatedBmh .Status .LastUpdated ).NotTo (BeNil (), "BMH status.lastUpdated should not be nil" )
172+ if initialLastUpdated != nil {
173+ g .Expect (updatedBmh .Status .LastUpdated .Before (initialLastUpdated )).To (BeFalse (),
174+ "BMH status.lastUpdated should have been updated by controller" )
175+ }
176+ }, intervals ... ).Should (Succeed ())
177+ }
178+
145179// DeleteBmhsInNamespace deletes all BMHs in the given namespace.
146180func DeleteBmhsInNamespace (ctx context.Context , deleter client.Client , namespace string ) {
147181 bmh := metal3api.BareMetalHost {}
0 commit comments