Skip to content

Commit f875f1d

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 f875f1d

3 files changed

Lines changed: 603 additions & 0 deletions

File tree

modules/common/object/metadata.go

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

26+
"github.com/openstack-k8s-operators/lib-common/modules/common/condition"
2627
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
2728
"k8s.io/apimachinery/pkg/runtime"
2829
"k8s.io/apimachinery/pkg/types"
2930
"sigs.k8s.io/controller-runtime/pkg/client"
3031
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3132

33+
apimeta "k8s.io/apimachinery/pkg/api/meta"
3234
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3335
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
37+
"k8s.io/apimachinery/pkg/runtime/schema"
3438
)
3539

3640
// CheckOwnerRefExist - returns true if the owner is already in the owner ref list
@@ -181,3 +185,108 @@ func ManageConsumerFinalizer(
181185

182186
return nil
183187
}
188+
189+
// IsOwnerReady checks if the controller owner of this object is ready.
190+
// Returns true if the owner is ready, false if not ready, and error only for unexpected failures.
191+
// If there's no owner with controller=true, it returns true (safe to proceed).
192+
func IsOwnerReady(
193+
ctx context.Context,
194+
h *helper.Helper,
195+
obj client.Object,
196+
) (bool, error) {
197+
// Find the controller owner reference (e.g., Cinder, Nova, etc.)
198+
var ownerRef *metav1.OwnerReference
199+
for _, owner := range obj.GetOwnerReferences() {
200+
if owner.Controller != nil && *owner.Controller {
201+
ownerRef = &owner
202+
break
203+
}
204+
}
205+
206+
// If no controlling owner, safe to proceed
207+
if ownerRef == nil {
208+
h.GetLogger().Info("No controller owner found, owner is considered ready")
209+
return true, nil
210+
}
211+
212+
// Parse the APIVersion to extract group and version
213+
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
214+
if err != nil {
215+
h.GetLogger().Error(err, "Failed to parse owner APIVersion", "apiVersion", ownerRef.APIVersion)
216+
return false, err
217+
}
218+
219+
// Fetch the owner resource using unstructured client
220+
owner := &unstructured.Unstructured{}
221+
owner.SetGroupVersionKind(schema.GroupVersionKind{
222+
Group: gv.Group,
223+
Version: gv.Version,
224+
Kind: ownerRef.Kind,
225+
})
226+
227+
err = h.GetClient().Get(ctx, types.NamespacedName{
228+
Name: ownerRef.Name,
229+
Namespace: obj.GetNamespace(),
230+
}, owner)
231+
232+
if err != nil {
233+
if k8s_errors.IsNotFound(err) || apimeta.IsNoMatchError(err) {
234+
h.GetLogger().Info("Owner resource not found, owner is considered ready", "kind", ownerRef.Kind, "name", ownerRef.Name)
235+
return true, nil
236+
}
237+
h.GetLogger().Error(err, "Failed to fetch owner resource", "kind", ownerRef.Kind, "name", ownerRef.Name)
238+
return false, err
239+
}
240+
241+
// Check status.conditions for Ready condition
242+
conditions, found, err := unstructured.NestedSlice(owner.Object, "status", "conditions")
243+
if err != nil || !found {
244+
h.GetLogger().Info("No conditions found in owner status, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
245+
return false, nil
246+
}
247+
248+
// Marshal unstructured conditions to condition.Conditions to use existing helper functions
249+
conditionsJSON, err := json.Marshal(conditions)
250+
if err != nil {
251+
h.GetLogger().Info("Failed to marshal owner conditions, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
252+
return false, nil
253+
}
254+
255+
var ownerConditions condition.Conditions
256+
err = json.Unmarshal(conditionsJSON, &ownerConditions)
257+
if err != nil {
258+
h.GetLogger().Info("Failed to unmarshal owner conditions, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
259+
return false, nil
260+
}
261+
262+
// Use existing helper function to check if Ready condition is True
263+
if !ownerConditions.IsTrue(condition.ReadyCondition) {
264+
h.GetLogger().Info("Owner service not ready, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
265+
return false, nil
266+
}
267+
268+
// Check if owner has reconciled (observedGeneration matches generation)
269+
generation, foundGen, err := unstructured.NestedInt64(owner.Object, "metadata", "generation")
270+
if err != nil || !foundGen {
271+
h.GetLogger().Info("Could not get owner generation, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
272+
return false, nil
273+
}
274+
275+
observedGeneration, foundObsGen, err := unstructured.NestedInt64(owner.Object, "status", "observedGeneration")
276+
if err != nil || !foundObsGen {
277+
h.GetLogger().Info("Could not get owner observedGeneration, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
278+
return false, nil
279+
}
280+
281+
if observedGeneration != generation {
282+
h.GetLogger().Info("Owner service has not reconciled yet (observedGeneration != generation), waiting",
283+
"kind", ownerRef.Kind,
284+
"name", ownerRef.Name,
285+
"generation", generation,
286+
"observedGeneration", observedGeneration)
287+
return false, nil
288+
}
289+
290+
h.GetLogger().Info("Owner service is ready and has reconciled, safe to proceed", "kind", ownerRef.Kind, "name", ownerRef.Name)
291+
return true, nil
292+
}

0 commit comments

Comments
 (0)