Skip to content

Commit bca756a

Browse files
committed
Add function to check the status of the owner of an object
Function verifies: - Ready condition is True - observedGeneration matches generation
1 parent d3778a5 commit bca756a

3 files changed

Lines changed: 609 additions & 0 deletions

File tree

modules/common/object/metadata.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ import (
2323
"fmt"
2424
"slices"
2525

26+
"github.com/go-logr/logr"
27+
"github.com/openstack-k8s-operators/lib-common/modules/common/condition"
2628
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
2729
"k8s.io/apimachinery/pkg/runtime"
2830
"k8s.io/apimachinery/pkg/types"
2931
"sigs.k8s.io/controller-runtime/pkg/client"
3032
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3133

3234
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
35+
apimeta "k8s.io/apimachinery/pkg/api/meta"
3336
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
38+
"k8s.io/apimachinery/pkg/runtime/schema"
3439
)
3540

3641
// CheckOwnerRefExist - returns true if the owner is already in the owner ref list
@@ -181,3 +186,113 @@ func ManageConsumerFinalizer(
181186

182187
return nil
183188
}
189+
190+
// IsOwnerReady checks if the controller owner of this object is ready.
191+
// It reads the owner via the supplied client.Reader — callers should pass an
192+
// uncached (API-server) reader to avoid stale informer data and phantom
193+
// informers for cross-operator types the calling controller does not watch.
194+
// Returns true if the owner is ready, false if not ready, and error only for
195+
// unexpected failures.
196+
// If there's no owner with controller=true, it returns true (safe to proceed).
197+
func IsOwnerReady(
198+
ctx context.Context,
199+
reader client.Reader,
200+
log logr.Logger,
201+
obj client.Object,
202+
) (bool, error) {
203+
var ownerRef *metav1.OwnerReference
204+
for _, owner := range obj.GetOwnerReferences() {
205+
if owner.Controller != nil && *owner.Controller {
206+
ownerRef = &owner
207+
break
208+
}
209+
}
210+
211+
if ownerRef == nil {
212+
log.V(1).Info("No controller owner found, owner is considered ready")
213+
return true, nil
214+
}
215+
216+
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
217+
if err != nil {
218+
log.Error(err, "Failed to parse owner APIVersion", "apiVersion", ownerRef.APIVersion)
219+
return false, err
220+
}
221+
222+
owner := &unstructured.Unstructured{}
223+
owner.SetGroupVersionKind(schema.GroupVersionKind{
224+
Group: gv.Group,
225+
Version: gv.Version,
226+
Kind: ownerRef.Kind,
227+
})
228+
229+
err = reader.Get(ctx, types.NamespacedName{
230+
Name: ownerRef.Name,
231+
Namespace: obj.GetNamespace(),
232+
}, owner)
233+
234+
if err != nil {
235+
if k8s_errors.IsNotFound(err) || apimeta.IsNoMatchError(err) {
236+
log.V(1).Info("Owner resource not found, owner is considered ready",
237+
"kind", ownerRef.Kind, "name", ownerRef.Name)
238+
return true, nil
239+
}
240+
log.Error(err, "Failed to fetch owner resource",
241+
"kind", ownerRef.Kind, "name", ownerRef.Name)
242+
return false, err
243+
}
244+
245+
conditions, found, err := unstructured.NestedSlice(owner.Object, "status", "conditions")
246+
if err != nil || !found {
247+
log.V(1).Info("No conditions found in owner status, waiting",
248+
"kind", ownerRef.Kind, "name", ownerRef.Name)
249+
return false, nil
250+
}
251+
252+
conditionsJSON, err := json.Marshal(conditions)
253+
if err != nil {
254+
log.V(1).Info("Failed to marshal owner conditions, waiting",
255+
"kind", ownerRef.Kind, "name", ownerRef.Name)
256+
return false, nil
257+
}
258+
259+
var ownerConditions condition.Conditions
260+
err = json.Unmarshal(conditionsJSON, &ownerConditions)
261+
if err != nil {
262+
log.V(1).Info("Failed to unmarshal owner conditions, waiting",
263+
"kind", ownerRef.Kind, "name", ownerRef.Name)
264+
return false, nil
265+
}
266+
267+
if !ownerConditions.IsTrue(condition.ReadyCondition) {
268+
log.V(1).Info("Owner not ready, waiting",
269+
"kind", ownerRef.Kind, "name", ownerRef.Name)
270+
return false, nil
271+
}
272+
273+
generation, foundGen, err := unstructured.NestedInt64(owner.Object, "metadata", "generation")
274+
if err != nil || !foundGen {
275+
log.V(1).Info("Could not get owner generation, waiting",
276+
"kind", ownerRef.Kind, "name", ownerRef.Name)
277+
return false, nil
278+
}
279+
280+
observedGeneration, foundObsGen, err := unstructured.NestedInt64(owner.Object, "status", "observedGeneration")
281+
if err != nil || !foundObsGen {
282+
log.V(1).Info("Could not get owner observedGeneration, waiting",
283+
"kind", ownerRef.Kind, "name", ownerRef.Name)
284+
return false, nil
285+
}
286+
287+
if observedGeneration != generation {
288+
log.V(1).Info("Owner has not reconciled yet, waiting",
289+
"kind", ownerRef.Kind,
290+
"name", ownerRef.Name,
291+
"generation", generation,
292+
"observedGeneration", observedGeneration)
293+
return false, nil
294+
}
295+
296+
log.Info("Owner is ready", "kind", ownerRef.Kind, "name", ownerRef.Name)
297+
return true, nil
298+
}

0 commit comments

Comments
 (0)