Skip to content

Commit 39e120c

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 ecff41e commit 39e120c

3 files changed

Lines changed: 604 additions & 0 deletions

File tree

modules/common/object/metadata.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ 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"
@@ -31,6 +32,8 @@ import (
3132

3233
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
36+
"k8s.io/apimachinery/pkg/runtime/schema"
3437
)
3538

3639
// CheckOwnerRefExist - returns true if the owner is already in the owner ref list
@@ -181,3 +184,110 @@ func ManageConsumerFinalizer(
181184

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

0 commit comments

Comments
 (0)