@@ -22,6 +22,7 @@ import (
2222
2323 . "github.com/onsi/ginkgo/v2"
2424 . "github.com/onsi/gomega"
25+ "k8s.io/apimachinery/pkg/api/resource"
2526 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627 "k8s.io/utils/ptr"
2728 virtv1 "kubevirt.io/api/core/v1"
@@ -41,24 +42,24 @@ import (
4142 "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition"
4243)
4344
44- var _ = Describe ("SyncKvvmHandler" , func () {
45+ var _ = FDescribe ("SyncKvvmHandler" , func () {
4546 const (
4647 name = "vm-sync"
4748 namespace = "default"
4849 )
4950
5051 var (
51- ctx context.Context
52- fakeClient client.WithWatch
53- resource * reconciler.Resource [* v1alpha2.VirtualMachine , v1alpha2.VirtualMachineStatus ]
54- vmState state.VirtualMachineState
55- recorder * eventrecord.EventRecorderLoggerMock
52+ ctx context.Context
53+ fakeClient client.WithWatch
54+ reconcileObj * reconciler.Resource [* v1alpha2.VirtualMachine , v1alpha2.VirtualMachineStatus ]
55+ vmState state.VirtualMachineState
56+ recorder * eventrecord.EventRecorderLoggerMock
5657 )
5758
5859 BeforeEach (func () {
5960 ctx = testutil .ContextBackgroundWithNoOpLogger ()
6061 fakeClient = nil
61- resource = nil
62+ reconcileObj = nil
6263 vmState = nil
6364 recorder = & eventrecord.EventRecorderLoggerMock {
6465 EventFunc : func (_ client.Object , _ , _ , _ string ) {},
@@ -69,27 +70,49 @@ var _ = Describe("SyncKvvmHandler", func() {
6970
7071 AfterEach (func () {
7172 fakeClient = nil
72- resource = nil
73+ reconcileObj = nil
7374 vmState = nil
7475 recorder = nil
7576 })
7677
77- newVM := func (phase v1alpha2.MachinePhase ) * v1alpha2.VirtualMachine {
78+ makeVM := func (phase v1alpha2.MachinePhase ) * v1alpha2.VirtualMachine {
7879 vm := vmbuilder .NewEmpty (name , namespace )
79- vm . Status . Phase = phase
80+
8081 vm .Spec .VirtualMachineClassName = "vmclass"
8182 vm .Spec .CPU .Cores = 2
83+ vm .Spec .Memory .Size = resource .MustParse ("2Gi" )
8284 vm .Spec .RunPolicy = v1alpha2 .ManualPolicy
8385 vm .Spec .VirtualMachineIPAddress = "test-ip"
8486 vm .Spec .OsType = v1alpha2 .GenericOs
8587 vm .Spec .Disruptions = & v1alpha2.Disruptions {
8688 RestartApprovalMode : v1alpha2 .Manual ,
8789 }
8890
91+ vm .Status .Phase = phase
92+
8993 return vm
9094 }
9195
92- newKVVM := func (vm * v1alpha2.VirtualMachine ) * virtv1.VirtualMachine {
96+ // It is like mapPhases in vm/internal/util.go but reversed.
97+ mapVMPhaseToKVVMPrintableStatus := func (phase v1alpha2.MachinePhase ) virtv1.VirtualMachinePrintableStatus {
98+ switch phase {
99+ case v1alpha2 .MachineRunning :
100+ return virtv1 .VirtualMachineStatusRunning
101+ case v1alpha2 .MachineMigrating :
102+ return virtv1 .VirtualMachineStatusMigrating
103+ case v1alpha2 .MachineStopping :
104+ return virtv1 .VirtualMachineStatusStopping
105+ case v1alpha2 .MachineStopped :
106+ return virtv1 .VirtualMachineStatusStopped
107+ case v1alpha2 .MachineStarting :
108+ return virtv1 .VirtualMachineStatusProvisioning
109+ case v1alpha2 .MachinePending :
110+ return virtv1 .VirtualMachineStatusUnknown
111+ }
112+ return ""
113+ }
114+
115+ makeKVVM := func (vm * v1alpha2.VirtualMachine ) * virtv1.VirtualMachine {
93116 kvvm := & virtv1.VirtualMachine {
94117 ObjectMeta : metav1.ObjectMeta {
95118 Name : name ,
@@ -101,11 +124,17 @@ var _ = Describe("SyncKvvmHandler", func() {
101124 }
102125 kvvm .Spec .RunStrategy = ptr .To (virtv1 .RunStrategyAlways )
103126
127+ // Printable status is required for proper detection if changes are disruptive.
128+ kvvm .Status .PrintableStatus = mapVMPhaseToKVVMPrintableStatus (vm .Status .Phase )
129+
104130 Expect (kvbuilder .SetLastAppliedSpec (kvvm , & v1alpha2.VirtualMachine {
105131 Spec : v1alpha2.VirtualMachineSpec {
106132 CPU : v1alpha2.CPUSpec {
107133 Cores : vm .Spec .CPU .Cores ,
108134 },
135+ Memory : v1alpha2.MemorySpec {
136+ Size : vm .Spec .Memory .Size ,
137+ },
109138 VirtualMachineIPAddress : vm .Spec .VirtualMachineIPAddress ,
110139 RunPolicy : vm .Spec .RunPolicy ,
111140 OsType : vm .Spec .OsType ,
@@ -132,16 +161,50 @@ var _ = Describe("SyncKvvmHandler", func() {
132161 return kvvm
133162 }
134163
135- newKVVMI := func () * virtv1.VirtualMachineInstance {
164+ makeKVVMI := func () * virtv1.VirtualMachineInstance {
136165 kvvmi := newEmptyKVVMI (name , namespace )
137166 return kvvmi
138167 }
139168
169+ makeVMIP := func () * v1alpha2.VirtualMachineIPAddress {
170+ return & v1alpha2.VirtualMachineIPAddress {
171+ ObjectMeta : metav1.ObjectMeta {
172+ Name : "test-ip" ,
173+ Namespace : namespace ,
174+ },
175+ Spec : v1alpha2.VirtualMachineIPAddressSpec {
176+ Type : v1alpha2 .VirtualMachineIPAddressTypeStatic ,
177+ StaticIP : "192.168.1.10" ,
178+ },
179+ Status : v1alpha2.VirtualMachineIPAddressStatus {
180+ Address : "192.168.1.10" ,
181+ Phase : v1alpha2 .VirtualMachineIPAddressPhaseAttached ,
182+ },
183+ }
184+ }
185+
186+ makeVMClass := func () * v1alpha2.VirtualMachineClass {
187+ return & v1alpha2.VirtualMachineClass {
188+ ObjectMeta : metav1.ObjectMeta {
189+ Name : "vmclass" ,
190+ }, Spec : v1alpha2.VirtualMachineClassSpec {
191+ CPU : v1alpha2.CPU {
192+ Type : v1alpha2 .CPUTypeHost ,
193+ },
194+ NodeSelector : v1alpha2.NodeSelector {
195+ MatchLabels : map [string ]string {
196+ "node1" : "node1" ,
197+ },
198+ },
199+ },
200+ }
201+ }
202+
140203 reconcile := func () {
141204 h := NewSyncKvvmHandler (nil , fakeClient , recorder , featuregates .Default (), vmservice .NewMigrationVolumesService (fakeClient , MakeKVVMFromVMSpec , 10 * time .Second ))
142205 _ , err := h .Handle (ctx , vmState )
143206 Expect (err ).NotTo (HaveOccurred ())
144- err = resource .Update (context .Background ())
207+ err = reconcileObj .Update (context .Background ())
145208 Expect (err ).NotTo (HaveOccurred ())
146209 }
147210
@@ -175,47 +238,33 @@ var _ = Describe("SyncKvvmHandler", func() {
175238 })).To (Succeed ())
176239 }
177240
241+ mutateCPUCores := func (cores int ) func (fakeClient client.WithWatch , vm * v1alpha2.VirtualMachine , kvvm * virtv1.VirtualMachine ) {
242+ return func (fakeClient client.WithWatch , vm * v1alpha2.VirtualMachine , kvvm * virtv1.VirtualMachine ) {
243+ vm .Spec .CPU .Cores = cores
244+ }
245+ }
246+
247+ mutateMemorySize := func (size string ) func (fakeClient client.WithWatch , vm * v1alpha2.VirtualMachine , kvvm * virtv1.VirtualMachine ) {
248+ memSize := resource .MustParse (size )
249+ return func (fakeClient client.WithWatch , vm * v1alpha2.VirtualMachine , kvvm * virtv1.VirtualMachine ) {
250+ vm .Spec .Memory .Size = memSize
251+ }
252+ }
253+
178254 DescribeTable ("AwaitingRestart Condition Tests" ,
179255 func (phase v1alpha2.MachinePhase , needChange bool , expectedStatus metav1.ConditionStatus , expectedExistence bool ) {
180- ip := & v1alpha2.VirtualMachineIPAddress {
181- ObjectMeta : metav1.ObjectMeta {
182- Name : "test-ip" ,
183- Namespace : namespace ,
184- },
185- Spec : v1alpha2.VirtualMachineIPAddressSpec {
186- Type : v1alpha2 .VirtualMachineIPAddressTypeStatic ,
187- StaticIP : "192.168.1.10" ,
188- },
189- Status : v1alpha2.VirtualMachineIPAddressStatus {
190- Address : "192.168.1.10" ,
191- Phase : v1alpha2 .VirtualMachineIPAddressPhaseAttached ,
192- },
193- }
256+ ip := makeVMIP ()
257+ vmClass := makeVMClass ()
194258
195- vmClass := & v1alpha2.VirtualMachineClass {
196- ObjectMeta : metav1.ObjectMeta {
197- Name : "vmclass" ,
198- }, Spec : v1alpha2.VirtualMachineClassSpec {
199- CPU : v1alpha2.CPU {
200- Type : v1alpha2 .CPUTypeHost ,
201- },
202- NodeSelector : v1alpha2.NodeSelector {
203- MatchLabels : map [string ]string {
204- "node1" : "node1" ,
205- },
206- },
207- },
208- }
209-
210- vm := newVM (phase )
211- kvvm := newKVVM (vm )
212- kvvmi := newKVVMI ()
259+ vm := makeVM (phase )
260+ kvvm := makeKVVM (vm )
261+ kvvmi := makeKVVMI ()
213262
214263 if needChange {
215264 mutateKVVM (vm , kvvm )
216265 }
217266
218- fakeClient , resource , vmState = setupEnvironment (vm , kvvm , kvvmi , ip , vmClass )
267+ fakeClient , reconcileObj , vmState = setupEnvironment (vm , kvvm , kvvmi , ip , vmClass )
219268
220269 reconcile ()
221270
@@ -248,49 +297,59 @@ var _ = Describe("SyncKvvmHandler", func() {
248297 Entry ("Pending phase without changes, shouldn't have condition" , v1alpha2 .MachinePending , false , metav1 .ConditionUnknown , false ),
249298 )
250299
251- DescribeTable ("ConfigurationApplied Condition Tests" ,
252- func (phase v1alpha2.MachinePhase , notReady bool , expectedStatus metav1.ConditionStatus , expectedExistence bool ) {
253- ip := & v1alpha2.VirtualMachineIPAddress {
254- ObjectMeta : metav1.ObjectMeta {
255- Name : "test-ip" ,
256- Namespace : namespace ,
257- },
258- Spec : v1alpha2.VirtualMachineIPAddressSpec {
259- Type : v1alpha2 .VirtualMachineIPAddressTypeStatic ,
260- StaticIP : "192.168.1.10" ,
261- },
262- Status : v1alpha2.VirtualMachineIPAddressStatus {
263- Address : "192.168.1.10" ,
264- Phase : v1alpha2 .VirtualMachineIPAddressPhaseAttached ,
265- },
300+ DescribeTable ("AwaitingRestart Condition for NonMigratable VM" ,
301+ func (phase v1alpha2.MachinePhase , mutateFn func (fakeClient client.WithWatch , vm * v1alpha2.VirtualMachine , kvvm * virtv1.VirtualMachine ), expectedStatus metav1.ConditionStatus , expectedExistence bool ) {
302+ ip := makeVMIP ()
303+ vmClass := makeVMClass ()
304+
305+ vm := makeVM (phase )
306+ vm .Status .Conditions = append (vm .Status .Conditions , metav1.Condition {
307+ Type : vmcondition .TypeMigratable .String (),
308+ Status : metav1 .ConditionFalse ,
309+ Reason : string (vmcondition .ReasonHostDevicesNotMigratable ),
310+ })
311+ kvvm := makeKVVM (vm )
312+ kvvmi := makeKVVMI ()
313+
314+ if mutateFn != nil {
315+ mutateFn (fakeClient , vm , kvvm )
266316 }
267317
268- vmClass := & v1alpha2.VirtualMachineClass {
269- ObjectMeta : metav1.ObjectMeta {
270- Name : "vmclass" ,
271- }, Spec : v1alpha2.VirtualMachineClassSpec {
272- CPU : v1alpha2.CPU {
273- Type : v1alpha2 .CPUTypeHost ,
274- },
275- NodeSelector : v1alpha2.NodeSelector {
276- MatchLabels : map [string ]string {
277- "node1" : "node1" ,
278- },
279- },
280- },
318+ fakeClient , reconcileObj , vmState = setupEnvironment (vm , kvvm , kvvmi , ip , vmClass )
319+
320+ reconcile ()
321+
322+ newVM := & v1alpha2.VirtualMachine {}
323+ err := fakeClient .Get (ctx , client .ObjectKeyFromObject (vm ), newVM )
324+ Expect (err ).NotTo (HaveOccurred ())
325+
326+ awaitCond , awaitExists := conditions .GetCondition (vmcondition .TypeAwaitingRestartToApplyConfiguration , newVM .Status .Conditions )
327+ Expect (awaitExists ).To (Equal (expectedExistence ))
328+ if awaitExists {
329+ Expect (awaitCond .Status ).To (Equal (expectedStatus ))
281330 }
331+ },
332+ Entry ("should present on hotplugging cpu.cores" , v1alpha2 .MachineRunning , mutateCPUCores (3 ), metav1 .ConditionTrue , true ),
333+ Entry ("should present on hotplugging memory.size" , v1alpha2 .MachineRunning , mutateMemorySize ("4Gi" ), metav1 .ConditionTrue , true ),
334+ )
335+
336+ DescribeTable ("ConfigurationApplied Condition Tests" ,
337+ func (phase v1alpha2.MachinePhase , notReady bool , expectedStatus metav1.ConditionStatus , expectedExistence bool ) {
338+ ip := makeVMIP ()
339+ vmClass := makeVMClass ()
282340
283- vm := newVM (phase )
341+ vm := makeVM (phase )
284342 if notReady {
285343 vm .Status .Conditions = append (vm .Status .Conditions , metav1.Condition {
286344 Type : vmcondition .TypeBlockDevicesReady .String (),
287345 Status : metav1 .ConditionFalse ,
288346 Reason : "BlockDevicesNotReady" ,
289347 })
290348 }
291- kvvm := newKVVM (vm )
349+ kvvm := makeKVVM (vm )
350+ kvvmi := makeKVVMI ()
292351
293- fakeClient , resource , vmState = setupEnvironment (vm , kvvm , ip , vmClass )
352+ fakeClient , reconcileObj , vmState = setupEnvironment (vm , kvvm , kvvmi , ip , vmClass )
294353 reconcile ()
295354
296355 newVM := & v1alpha2.VirtualMachine {}
0 commit comments