Skip to content

Commit 35259aa

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 6ba873b commit 35259aa

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
@@ -114,3 +117,110 @@ func EnsureOwnerRef(
114117

115118
return nil
116119
}
120+
121+
// IsOwnerServiceReady checks if the owner service that owns this object is ready.
122+
// Returns true if the owner is ready, false if not ready, and error only for unexpected failures.
123+
// If there's no owner with controller=true, it returns true (safe to proceed).
124+
func IsOwnerServiceReady(
125+
ctx context.Context,
126+
h *helper.Helper,
127+
obj client.Object,
128+
) (bool, error) {
129+
// Find the controller owner reference (e.g., Cinder, Nova, etc.)
130+
var ownerRef *metav1.OwnerReference
131+
for _, owner := range obj.GetOwnerReferences() {
132+
if owner.Controller != nil && *owner.Controller {
133+
ownerRef = &owner
134+
break
135+
}
136+
}
137+
138+
// If no controlling owner, safe to proceed
139+
if ownerRef == nil {
140+
h.GetLogger().Info("No controller owner found, owner is considered ready")
141+
return true, nil
142+
}
143+
144+
// Parse the APIVersion to extract group and version
145+
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
146+
if err != nil {
147+
h.GetLogger().Error(err, "Failed to parse owner APIVersion", "apiVersion", ownerRef.APIVersion)
148+
return false, err
149+
}
150+
151+
// Fetch the owner resource using unstructured client
152+
owner := &unstructured.Unstructured{}
153+
owner.SetGroupVersionKind(schema.GroupVersionKind{
154+
Group: gv.Group,
155+
Version: gv.Version,
156+
Kind: ownerRef.Kind,
157+
})
158+
159+
err = h.GetClient().Get(ctx, types.NamespacedName{
160+
Name: ownerRef.Name,
161+
Namespace: obj.GetNamespace(),
162+
}, owner)
163+
164+
if err != nil {
165+
if k8s_errors.IsNotFound(err) {
166+
// Owner deleted, safe to proceed
167+
h.GetLogger().Info("Owner resource not found, owner is considered ready", "kind", ownerRef.Kind, "name", ownerRef.Name)
168+
return true, nil
169+
}
170+
// Unexpected error, log and return error
171+
h.GetLogger().Error(err, "Failed to fetch owner resource", "kind", ownerRef.Kind, "name", ownerRef.Name)
172+
return false, err
173+
}
174+
175+
// Check status.conditions for Ready condition
176+
conditions, found, err := unstructured.NestedSlice(owner.Object, "status", "conditions")
177+
if err != nil || !found {
178+
h.GetLogger().Info("No conditions found in owner status, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
179+
return false, nil
180+
}
181+
182+
// Marshal unstructured conditions to condition.Conditions to use existing helper functions
183+
conditionsJSON, err := json.Marshal(conditions)
184+
if err != nil {
185+
h.GetLogger().Info("Failed to marshal owner conditions, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
186+
return false, nil
187+
}
188+
189+
var ownerConditions condition.Conditions
190+
err = json.Unmarshal(conditionsJSON, &ownerConditions)
191+
if err != nil {
192+
h.GetLogger().Info("Failed to unmarshal owner conditions, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
193+
return false, nil
194+
}
195+
196+
// Use existing helper function to check if Ready condition is True
197+
if !ownerConditions.IsTrue(condition.ReadyCondition) {
198+
h.GetLogger().Info("Owner service not ready, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
199+
return false, nil
200+
}
201+
202+
// Check if owner has reconciled (observedGeneration matches generation)
203+
generation, foundGen, err := unstructured.NestedInt64(owner.Object, "metadata", "generation")
204+
if err != nil || !foundGen {
205+
h.GetLogger().Info("Could not get owner generation, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
206+
return false, nil
207+
}
208+
209+
observedGeneration, foundObsGen, err := unstructured.NestedInt64(owner.Object, "status", "observedGeneration")
210+
if err != nil || !foundObsGen {
211+
h.GetLogger().Info("Could not get owner observedGeneration, waiting", "kind", ownerRef.Kind, "name", ownerRef.Name)
212+
return false, nil
213+
}
214+
215+
if observedGeneration != generation {
216+
h.GetLogger().Info("Owner service has not reconciled yet (observedGeneration != generation), waiting",
217+
"kind", ownerRef.Kind,
218+
"name", ownerRef.Name,
219+
"generation", generation,
220+
"observedGeneration", observedGeneration)
221+
return false, nil
222+
}
223+
224+
h.GetLogger().Info("Owner service is ready and has reconciled, safe to proceed", "kind", ownerRef.Kind, "name", ownerRef.Name)
225+
return true, nil
226+
}

0 commit comments

Comments
 (0)