@@ -20,6 +20,7 @@ package controller
2020import (
2121 "context"
2222 "errors"
23+ "strings"
2324 "time"
2425
2526 kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
@@ -45,6 +46,26 @@ var _ = Describe("Hypervisor Controller", func() {
4546 Context ("When testing Start method" , func () {
4647 It ("should successfully start and subscribe to libvirt events" , func () {
4748 ctx := context .Background ()
49+
50+ // Create a hypervisor resource for this test
51+ hypervisorName := "start-success-test-hypervisor"
52+ originalHostname := sys .Hostname
53+ sys .Hostname = hypervisorName
54+ defer func () {
55+ sys .Hostname = originalHostname
56+ }()
57+
58+ hypervisor := & kvmv1.Hypervisor {
59+ ObjectMeta : metav1.ObjectMeta {
60+ Name : hypervisorName ,
61+ },
62+ }
63+ Expect (k8sClient .Create (ctx , hypervisor )).To (Succeed ())
64+ defer func () {
65+ err := k8sClient .Delete (ctx , hypervisor )
66+ Expect (err ).NotTo (HaveOccurred ())
67+ }()
68+
4869 eventCallbackCalled := false
4970
5071 controllerReconciler := & HypervisorReconciler {
@@ -68,7 +89,27 @@ var _ = Describe("Hypervisor Controller", func() {
6889 })
6990
7091 It ("should fail when libvirt connection fails" , func () {
71- ctx := context .Background ()
92+ ctx , cancel := context .WithCancel (context .Background ())
93+ defer cancel ()
94+
95+ // Create a hypervisor resource for this test
96+ hypervisorName := "start-fail-test-hypervisor"
97+ originalHostname := sys .Hostname
98+ sys .Hostname = hypervisorName
99+ defer func () {
100+ sys .Hostname = originalHostname
101+ }()
102+
103+ hypervisor := & kvmv1.Hypervisor {
104+ ObjectMeta : metav1.ObjectMeta {
105+ Name : hypervisorName ,
106+ },
107+ }
108+ Expect (k8sClient .Create (context .Background (), hypervisor )).To (Succeed ())
109+ defer func () {
110+ err := k8sClient .Delete (context .Background (), hypervisor )
111+ Expect (err ).NotTo (HaveOccurred ())
112+ }()
72113
73114 controllerReconciler := & HypervisorReconciler {
74115 Client : k8sClient ,
@@ -78,12 +119,124 @@ var _ = Describe("Hypervisor Controller", func() {
78119 return errors .New ("connection failed" )
79120 },
80121 },
122+ reconcileCh : make (chan event.GenericEvent , 1 ),
123+ libvirtConnectInterval : 10 * time .Millisecond ,
124+ }
125+
126+ // Start runs in a goroutine so we can cancel the context
127+ done := make (chan error , 1 )
128+ go func () {
129+ done <- controllerReconciler .Start (ctx )
130+ }()
131+
132+ // Wait for the hypervisor status to reflect the failed libvirt connection
133+ // This must happen BEFORE we cancel the context to ensure the Start method
134+ // had time to attempt connection and update the status
135+ var updatedHypervisor kvmv1.Hypervisor
136+ Eventually (func () bool {
137+ err := k8sClient .Get (context .Background (), types.NamespacedName {Name : hypervisorName }, & updatedHypervisor )
138+ if err != nil {
139+ return false
140+ }
141+ for _ , condition := range updatedHypervisor .Status .Conditions {
142+ if condition .Type == "LibVirtConnection" {
143+ return condition .Status == metav1 .ConditionFalse &&
144+ condition .Reason == "ConnectFailed" &&
145+ strings .Contains (condition .Message , "connection failed" )
146+ }
147+ }
148+ return false
149+ }, 5 * time .Second , 100 * time .Millisecond ).Should (BeTrue (), "hypervisor status should reflect failed libvirt connection" )
150+
151+ // Cancel the context to stop the Start method
152+ cancel ()
153+
154+ // Wait for Start to return with context cancellation error
155+ select {
156+ case err := <- done :
157+ Expect (err ).To (HaveOccurred ())
158+ Expect (err .Error ()).To (ContainSubstring ("context done while trying to connect to libvirt" ))
159+ case <- time .After (2 * time .Second ):
160+ Fail ("timeout waiting for Start to return after context cancellation" )
161+ }
162+ })
163+
164+ It ("should fail when hypervisor resource does not exist" , func () {
165+ ctx := context .Background ()
166+
167+ // Set hostname to a non-existent hypervisor
168+ originalHostname := sys .Hostname
169+ sys .Hostname = "non-existent-hypervisor"
170+ defer func () {
171+ sys .Hostname = originalHostname
172+ }()
173+
174+ controllerReconciler := & HypervisorReconciler {
175+ Client : k8sClient ,
176+ Scheme : k8sClient .Scheme (),
177+ Libvirt : & libvirt.InterfaceMock {
178+ ConnectFunc : func () error {
179+ return nil
180+ },
181+ },
81182 reconcileCh : make (chan event.GenericEvent , 1 ),
82183 }
83184
84185 err := controllerReconciler .Start (ctx )
85186 Expect (err ).To (HaveOccurred ())
86- Expect (err .Error ()).To (ContainSubstring ("connection failed" ))
187+ Expect (err .Error ()).To (ContainSubstring ("unable to get hypervisor" ))
188+ })
189+
190+ It ("should retry libvirt connection and succeed after initial failures" , func () {
191+ ctx := context .Background ()
192+
193+ // Create a hypervisor resource for this test
194+ hypervisorName := "start-retry-test-hypervisor"
195+ originalHostname := sys .Hostname
196+ sys .Hostname = hypervisorName
197+ defer func () {
198+ sys .Hostname = originalHostname
199+ }()
200+
201+ hypervisor := & kvmv1.Hypervisor {
202+ ObjectMeta : metav1.ObjectMeta {
203+ Name : hypervisorName ,
204+ },
205+ }
206+ Expect (k8sClient .Create (ctx , hypervisor )).To (Succeed ())
207+ defer func () {
208+ err := k8sClient .Delete (ctx , hypervisor )
209+ Expect (err ).NotTo (HaveOccurred ())
210+ }()
211+
212+ // Track connection attempts
213+ connectAttempts := 0
214+ eventCallbackCalled := false
215+
216+ controllerReconciler := & HypervisorReconciler {
217+ Client : k8sClient ,
218+ Scheme : k8sClient .Scheme (),
219+ Libvirt : & libvirt.InterfaceMock {
220+ ConnectFunc : func () error {
221+ connectAttempts ++
222+ // Fail first 2 attempts, succeed on 3rd
223+ if connectAttempts < 3 {
224+ return errors .New ("connection failed" )
225+ }
226+ return nil
227+ },
228+ WatchDomainChangesFunc : func (eventId golibvirt.DomainEventID , handlerId string , handler func (context.Context , any )) {
229+ eventCallbackCalled = true
230+ },
231+ },
232+ reconcileCh : make (chan event.GenericEvent , 1 ),
233+ libvirtConnectInterval : 10 * time .Millisecond , // Use short interval for fast test
234+ }
235+
236+ err := controllerReconciler .Start (ctx )
237+ Expect (err ).NotTo (HaveOccurred ())
238+ Expect (connectAttempts ).To (Equal (3 ))
239+ Expect (eventCallbackCalled ).To (BeTrue ())
87240 })
88241 })
89242
0 commit comments