@@ -21,6 +21,7 @@ import (
2121 . "github.com/onsi/gomega" // nolint:revive
2222 "github.com/openstack-k8s-operators/lib-common/modules/common/object"
2323
24+ corev1 "k8s.io/api/core/v1"
2425 "k8s.io/apimachinery/pkg/types"
2526)
2627
@@ -72,4 +73,200 @@ var _ = Describe("object package", func() {
7273 Expect (err ).ShouldNot (HaveOccurred ())
7374 Expect (object .CheckOwnerRefExist (ownerCM .GetUID (), cm .GetOwnerReferences ())).To (BeTrue ())
7475 })
76+
77+ When ("checking if owner service is ready" , func () {
78+ It ("returns true when object has no controller owner" , func () {
79+ cmName := types.NamespacedName {
80+ Namespace : namespace ,
81+ Name : "test-cm-no-owner" ,
82+ }
83+ cm := th .CreateConfigMap (cmName , map [string ]interface {}{})
84+
85+ ready , err := object .IsOwnerServiceReady (th .Ctx , h , cm )
86+ Expect (err ).ShouldNot (HaveOccurred ())
87+ Expect (ready ).To (BeTrue ())
88+ })
89+
90+ It ("returns true when controller owner is deleted" , func () {
91+ // Create a ConfigMap with an owner reference to a non-existent ConfigMap
92+ rawCM := map [string ]interface {}{
93+ "apiVersion" : "v1" ,
94+ "kind" : "ConfigMap" ,
95+ "metadata" : map [string ]interface {}{
96+ "name" : "test-cm-deleted-owner" ,
97+ "namespace" : namespace ,
98+ "ownerReferences" : []interface {}{
99+ map [string ]interface {}{
100+ "apiVersion" : "v1" ,
101+ "kind" : "ConfigMap" ,
102+ "name" : "non-existent-configmap" ,
103+ "uid" : "11111111-1111-1111-1111-111111111111" ,
104+ "controller" : true ,
105+ },
106+ },
107+ },
108+ }
109+ cm := th .CreateUnstructured (rawCM )
110+
111+ ready , err := object .IsOwnerServiceReady (th .Ctx , h , cm )
112+ Expect (err ).ShouldNot (HaveOccurred ())
113+ Expect (ready ).To (BeTrue ())
114+ })
115+
116+ It ("returns false when controller owner exists but has no status" , func () {
117+ // Create owner ConfigMap (no status.conditions)
118+ ownerCM := th .CreateConfigMap (types.NamespacedName {
119+ Namespace : namespace ,
120+ Name : "owner-cm-no-status" ,
121+ }, map [string ]interface {}{})
122+
123+ // Create child ConfigMap with owner reference
124+ rawCM := map [string ]interface {}{
125+ "apiVersion" : "v1" ,
126+ "kind" : "ConfigMap" ,
127+ "metadata" : map [string ]interface {}{
128+ "name" : "child-cm-no-status" ,
129+ "namespace" : namespace ,
130+ "ownerReferences" : []interface {}{
131+ map [string ]interface {}{
132+ "apiVersion" : "v1" ,
133+ "kind" : "ConfigMap" ,
134+ "name" : ownerCM .GetName (),
135+ "uid" : string (ownerCM .GetUID ()),
136+ "controller" : true ,
137+ },
138+ },
139+ },
140+ }
141+ cm := th .CreateUnstructured (rawCM )
142+
143+ ready , err := object .IsOwnerServiceReady (th .Ctx , h , cm )
144+ Expect (err ).ShouldNot (HaveOccurred ())
145+ // ConfigMaps don't have status.conditions, so should return false
146+ Expect (ready ).To (BeFalse ())
147+ })
148+
149+ It ("returns true when controller owner has Ready condition with status True" , func () {
150+ // Create a Pod as the owner (Pods support status updates)
151+ th .CreatePod (types.NamespacedName {
152+ Namespace : namespace ,
153+ Name : "owner-pod-ready" ,
154+ }, map [string ]string {}, map [string ]interface {}{
155+ "containers" : []interface {}{
156+ map [string ]interface {}{
157+ "name" : "test" ,
158+ "image" : "test:latest" ,
159+ },
160+ },
161+ })
162+
163+ // Update the Pod status to include a Ready condition
164+ Eventually (func (g Gomega ) {
165+ pod := th .GetPod (types.NamespacedName {
166+ Namespace : namespace ,
167+ Name : "owner-pod-ready" ,
168+ })
169+ // Manually set status with Ready condition
170+ pod .Status .Conditions = []corev1.PodCondition {
171+ {
172+ Type : "Ready" ,
173+ Status : corev1 .ConditionTrue ,
174+ },
175+ }
176+ err := th .K8sClient .Status ().Update (th .Ctx , pod )
177+ g .Expect (err ).ShouldNot (HaveOccurred ())
178+ }, th .Timeout , th .Interval ).Should (Succeed ())
179+
180+ // Get the updated pod to get its UID
181+ pod := th .GetPod (types.NamespacedName {
182+ Namespace : namespace ,
183+ Name : "owner-pod-ready" ,
184+ })
185+
186+ // Create a ConfigMap owned by the Pod
187+ rawCM := map [string ]interface {}{
188+ "apiVersion" : "v1" ,
189+ "kind" : "ConfigMap" ,
190+ "metadata" : map [string ]interface {}{
191+ "name" : "child-cm-ready-owner" ,
192+ "namespace" : namespace ,
193+ "ownerReferences" : []interface {}{
194+ map [string ]interface {}{
195+ "apiVersion" : "v1" ,
196+ "kind" : "Pod" ,
197+ "name" : pod .Name ,
198+ "uid" : string (pod .GetUID ()),
199+ "controller" : true ,
200+ },
201+ },
202+ },
203+ }
204+ cm := th .CreateUnstructured (rawCM )
205+
206+ ready , err := object .IsOwnerServiceReady (th .Ctx , h , cm )
207+ Expect (err ).ShouldNot (HaveOccurred ())
208+ Expect (ready ).To (BeTrue ())
209+ })
210+
211+ It ("returns false when controller owner has Ready condition with status False" , func () {
212+ // Create a Pod as the owner
213+ th .CreatePod (types.NamespacedName {
214+ Namespace : namespace ,
215+ Name : "owner-pod-not-ready" ,
216+ }, map [string ]string {}, map [string ]interface {}{
217+ "containers" : []interface {}{
218+ map [string ]interface {}{
219+ "name" : "test" ,
220+ "image" : "test:latest" ,
221+ },
222+ },
223+ })
224+
225+ // Update the Pod status to include a Ready=False condition
226+ Eventually (func (g Gomega ) {
227+ pod := th .GetPod (types.NamespacedName {
228+ Namespace : namespace ,
229+ Name : "owner-pod-not-ready" ,
230+ })
231+ pod .Status .Conditions = []corev1.PodCondition {
232+ {
233+ Type : "Ready" ,
234+ Status : corev1 .ConditionFalse ,
235+ },
236+ }
237+ err := th .K8sClient .Status ().Update (th .Ctx , pod )
238+ g .Expect (err ).ShouldNot (HaveOccurred ())
239+ }, th .Timeout , th .Interval ).Should (Succeed ())
240+
241+ // Get the updated pod
242+ pod := th .GetPod (types.NamespacedName {
243+ Namespace : namespace ,
244+ Name : "owner-pod-not-ready" ,
245+ })
246+
247+ // Create a ConfigMap owned by the Pod
248+ rawCM := map [string ]interface {}{
249+ "apiVersion" : "v1" ,
250+ "kind" : "ConfigMap" ,
251+ "metadata" : map [string ]interface {}{
252+ "name" : "child-cm-not-ready-owner" ,
253+ "namespace" : namespace ,
254+ "ownerReferences" : []interface {}{
255+ map [string ]interface {}{
256+ "apiVersion" : "v1" ,
257+ "kind" : "Pod" ,
258+ "name" : pod .Name ,
259+ "uid" : string (pod .GetUID ()),
260+ "controller" : true ,
261+ },
262+ },
263+ },
264+ }
265+ cm := th .CreateUnstructured (rawCM )
266+
267+ ready , err := object .IsOwnerServiceReady (th .Ctx , h , cm )
268+ Expect (err ).ShouldNot (HaveOccurred ())
269+ Expect (ready ).To (BeFalse ())
270+ })
271+ })
75272})
0 commit comments