1
19
20 package api
21
22 import (
23 "bytes"
24 "encoding/json"
25 "fmt"
26 "text/template"
27
28 . "github.com/onsi/ginkgo/v2"
29 . "github.com/onsi/gomega"
30 v1 "k8s.io/api/core/v1"
31 "k8s.io/utils/pointer"
32
33 v12 "kubevirt.io/api/core/v1"
34 )
35
36 type NetworkTemplateConfig struct {
37 InterfaceConfig string
38 }
39
40 var exampleJSONFmt = `{
41 "kind": "VirtualMachineInstance",
42 "apiVersion": "kubevirt.io/%s",
43 "metadata": {
44 "name": "testvmi",
45 "namespace": "default",
46 "selfLink": "/apis/kubevirt.io/%s/namespaces/default/virtualmachineinstances/testvmi",
47 "creationTimestamp": null
48 },
49 "spec": {
50 "domain": {
51 "resources": {
52 "requests": {
53 "memory": "8Mi"
54 }
55 },
56 "cpu": {
57 "cores": 3,
58 "sockets": 1,
59 "threads": 1,
60 "model": "Conroe",
61 "features": [
62 {
63 "name": "pcid",
64 "policy": "require"
65 },
66 {
67 "name": "monitor",
68 "policy": "disable"
69 }
70 ],
71 "dedicatedCpuPlacement": true
72 },
73 "firmware": {
74 "uuid": "28a42a60-44ef-4428-9c10-1a6aee94627f"
75 },
76 "clock": {
77 "utc": {},
78 "timer": {
79 "hpet": {
80 "present": true
81 },
82 "kvm": {
83 "present": true
84 },
85 "pit": {
86 "present": true
87 },
88 "rtc": {
89 "present": true
90 },
91 "hyperv": {
92 "present": true
93 }
94 }
95 },
96 "features": {
97 "acpi": {
98 "enabled": false
99 },
100 "apic": {
101 "enabled": true
102 },
103 "hyperv": {
104 "relaxed": {
105 "enabled": true
106 },
107 "vapic": {
108 "enabled": false
109 },
110 "spinlocks": {
111 "enabled": true,
112 "spinlocks": 4096
113 },
114 "vpindex": {
115 "enabled": false
116 },
117 "runtime": {
118 "enabled": true
119 },
120 "synic": {
121 "enabled": false
122 },
123 "synictimer": {
124 "enabled": true,
125 "direct": {
126 "enabled": true
127 }
128 },
129 "reset": {
130 "enabled": false
131 },
132 "vendorid": {
133 "enabled": true,
134 "vendorid": "vendor"
135 },
136 "frequencies": {
137 "enabled": false
138 },
139 "reenlightenment": {
140 "enabled": false
141 },
142 "tlbflush": {
143 "enabled": true
144 }
145 },
146 "smm": {
147 "enabled": true
148 },
149 "kvm": {
150 "hidden": true
151 },
152 "pvspinlock": {
153 "enabled": false
154 }
155 },
156 "devices": {
157 "disks": [
158 {
159 "name": "disk0",
160 "disk": {
161 "bus": "virtio"
162 },
163 "dedicatedIOThread": true
164 },
165 {
166 "name": "cdrom0",
167 "cdrom": {
168 "bus": "virtio",
169 "readonly": true,
170 "tray": "open"
171 }
172 },
173 {
174 "name": "lun0",
175 "lun": {
176 "bus": "virtio",
177 "readonly": true
178 }
179 },
180 {
181 "name": "disk1",
182 "disk": {
183 "bus": "virtio"
184 },
185 "serial": "sn-11223344"
186 }
187 ],
188 "interfaces": [
189 {
190 "name": "default",
191 {{.InterfaceConfig}}
192 }
193 ],
194 "inputs": [
195 {
196 "bus": "virtio",
197 "type": "tablet",
198 "name": "tablet0"
199 }
200 ],
201 "rng": {},
202 "blockMultiQueue": true
203 },
204 "ioThreadsPolicy": "shared"
205 },
206 "volumes": [
207 {
208 "name": "disk0",
209 "containerDisk": {
210 "image": "test/image",
211 "path": "/disk.img"
212 }
213 },
214 {
215 "name": "cdrom0",
216 "cloudInitNoCloud": {
217 "secretRef": {
218 "name": "testsecret"
219 },
220 "networkDataSecretRef": {
221 "name": "testnetworksecret"
222 }
223 }
224 }
225 ],
226 "networks": [
227 {
228 "name": "default",
229 "pod": {}
230 }
231 ]
232 },
233 "status": {
234 "guestOSInfo": {},
235 "runtimeUser": 0
236 }
237 }`
238
239 var exampleJSON = fmt.Sprintf(exampleJSONFmt, v12.ApiLatestVersion, v12.ApiLatestVersion)
240
241 var _ = Describe("Schema", func() {
242
243 var exampleVMI *v12.VirtualMachineInstance
244
245 BeforeEach(func() {
246 exampleVMI = NewMinimalVMI("testvmi")
247
248 pointer.BoolPtr(true)
249 exampleVMI.Spec.Domain.Devices.Disks = []v12.Disk{
250 {
251 Name: "disk0",
252 DiskDevice: v12.DiskDevice{
253 Disk: &v12.DiskTarget{
254 Bus: v12.VirtIO,
255 ReadOnly: false,
256 },
257 },
258 DedicatedIOThread: pointer.BoolPtr(true),
259 },
260 {
261 Name: "cdrom0",
262 DiskDevice: v12.DiskDevice{
263 CDRom: &v12.CDRomTarget{
264 Bus: v12.VirtIO,
265 ReadOnly: pointer.BoolPtr(true),
266 Tray: "open",
267 },
268 },
269 },
270 {
271 Name: "lun0",
272 DiskDevice: v12.DiskDevice{
273 LUN: &v12.LunTarget{
274 Bus: v12.VirtIO,
275 ReadOnly: true,
276 },
277 },
278 },
279 {
280 Name: "disk1",
281 Serial: "sn-11223344",
282 DiskDevice: v12.DiskDevice{
283 Disk: &v12.DiskTarget{
284 Bus: v12.VirtIO,
285 ReadOnly: false,
286 },
287 },
288 },
289 }
290
291 exampleVMI.Spec.Domain.Devices.Rng = &v12.Rng{}
292 exampleVMI.Spec.Domain.Devices.Inputs = []v12.Input{
293 {
294 Bus: v12.VirtIO,
295 Type: "tablet",
296 Name: "tablet0",
297 },
298 }
299 exampleVMI.Spec.Domain.Devices.BlockMultiQueue = pointer.BoolPtr(true)
300
301 exampleVMI.Spec.Volumes = []v12.Volume{
302 {
303 Name: "disk0",
304 VolumeSource: v12.VolumeSource{
305 ContainerDisk: &v12.ContainerDiskSource{
306 Image: "test/image",
307 Path: "/disk.img",
308 },
309 },
310 },
311 {
312 Name: "cdrom0",
313 VolumeSource: v12.VolumeSource{
314 CloudInitNoCloud: &v12.CloudInitNoCloudSource{
315 UserDataSecretRef: &v1.LocalObjectReference{
316 Name: "testsecret",
317 },
318 NetworkDataSecretRef: &v1.LocalObjectReference{
319 Name: "testnetworksecret",
320 },
321 },
322 },
323 },
324 }
325 exampleVMI.Spec.Domain.Features = &v12.Features{
326 ACPI: v12.FeatureState{Enabled: pointer.BoolPtr(false)},
327 SMM: &v12.FeatureState{Enabled: pointer.BoolPtr(true)},
328 APIC: &v12.FeatureAPIC{Enabled: pointer.BoolPtr(true)},
329 KVM: &v12.FeatureKVM{Hidden: true},
330 Pvspinlock: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
331 Hyperv: &v12.FeatureHyperv{
332 Relaxed: &v12.FeatureState{Enabled: pointer.BoolPtr(true)},
333 VAPIC: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
334 Spinlocks: &v12.FeatureSpinlocks{Enabled: pointer.BoolPtr(true)},
335 VPIndex: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
336 Runtime: &v12.FeatureState{Enabled: pointer.BoolPtr(true)},
337 SyNIC: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
338 SyNICTimer: &v12.SyNICTimer{Enabled: pointer.BoolPtr(true), Direct: &v12.FeatureState{Enabled: pointer.BoolPtr(true)}},
339 Reset: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
340 VendorID: &v12.FeatureVendorID{Enabled: pointer.BoolPtr(true), VendorID: "vendor"},
341 Frequencies: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
342 Reenlightenment: &v12.FeatureState{Enabled: pointer.BoolPtr(false)},
343 TLBFlush: &v12.FeatureState{Enabled: pointer.BoolPtr(true)},
344 },
345 }
346 exampleVMI.Spec.Domain.Clock = &v12.Clock{
347 ClockOffset: v12.ClockOffset{
348 UTC: &v12.ClockOffsetUTC{},
349 },
350 Timer: &v12.Timer{
351 HPET: &v12.HPETTimer{},
352 KVM: &v12.KVMTimer{},
353 PIT: &v12.PITTimer{},
354 RTC: &v12.RTCTimer{},
355 Hyperv: &v12.HypervTimer{},
356 },
357 }
358 exampleVMI.Spec.Domain.Firmware = &v12.Firmware{
359 UUID: "28a42a60-44ef-4428-9c10-1a6aee94627f",
360 }
361 exampleVMI.Spec.Domain.CPU = &v12.CPU{
362 Cores: 3,
363 Sockets: 1,
364 Threads: 1,
365 Model: "Conroe",
366 Features: []v12.CPUFeature{
367 {
368 Name: "pcid",
369 Policy: "require",
370 },
371 {
372 Name: "monitor",
373 Policy: "disable",
374 },
375 },
376 DedicatedCPUPlacement: true,
377 }
378 exampleVMI.Spec.Networks = []v12.Network{
379 v12.Network{
380 Name: "default",
381 NetworkSource: v12.NetworkSource{
382 Pod: &v12.PodNetwork{},
383 },
384 },
385 }
386
387 policy := v12.IOThreadsPolicyShared
388 exampleVMI.Spec.Domain.IOThreadsPolicy = &policy
389
390 v12.SetObjectDefaults_VirtualMachineInstance(exampleVMI)
391 })
392 Context("With example schema in json use pod network and bridge interface", func() {
393 It("Unmarshal json into struct", func() {
394 exampleVMI.Spec.Domain.Devices.Interfaces = []v12.Interface{
395 v12.Interface{
396 Name: "default",
397 InterfaceBindingMethod: v12.InterfaceBindingMethod{
398 Bridge: &v12.InterfaceBridge{},
399 },
400 },
401 }
402 networkTemplateData := NetworkTemplateConfig{InterfaceConfig: `"bridge": {}`}
403 tmpl, err := template.New("vmexample").Parse(exampleJSON)
404 Expect(err).ToNot(HaveOccurred())
405 var tpl bytes.Buffer
406 err = tmpl.Execute(&tpl, networkTemplateData)
407 Expect(err).ToNot(HaveOccurred())
408 newVMI := &v12.VirtualMachineInstance{}
409 err = json.Unmarshal(tpl.Bytes(), newVMI)
410 Expect(err).ToNot(HaveOccurred())
411 Expect(newVMI).To(Equal(exampleVMI))
412 })
413 It("Marshal struct into json", func() {
414 exampleVMI.Spec.Domain.Devices.Interfaces = []v12.Interface{
415 v12.Interface{
416 Name: "default",
417 InterfaceBindingMethod: v12.InterfaceBindingMethod{
418 Bridge: &v12.InterfaceBridge{},
419 },
420 },
421 }
422
423 networkTemplateData := NetworkTemplateConfig{InterfaceConfig: `"bridge": {}`}
424 tmpl, err := template.New("vmexample").Parse(exampleJSON)
425 Expect(err).ToNot(HaveOccurred())
426 var tpl bytes.Buffer
427 err = tmpl.Execute(&tpl, networkTemplateData)
428 Expect(err).ToNot(HaveOccurred())
429 exampleJSONParsed := tpl.String()
430 buf, err := json.MarshalIndent(*exampleVMI, "", " ")
431 Expect(err).ToNot(HaveOccurred())
432 Expect(string(buf)).To(Equal(exampleJSONParsed))
433 })
434 })
435 Context("With example schema in json use pod network and slirp interface", func() {
436 It("Unmarshal json into struct", func() {
437 exampleVMI.Spec.Domain.Devices.Interfaces = []v12.Interface{
438 v12.Interface{
439 Name: "default",
440 InterfaceBindingMethod: v12.InterfaceBindingMethod{
441 DeprecatedSlirp: &v12.DeprecatedInterfaceSlirp{},
442 },
443 },
444 }
445 networkTemplateData := NetworkTemplateConfig{InterfaceConfig: `"slirp": {}`}
446 tmpl, err := template.New("vmexample").Parse(exampleJSON)
447 Expect(err).ToNot(HaveOccurred())
448 var tpl bytes.Buffer
449 err = tmpl.Execute(&tpl, networkTemplateData)
450 Expect(err).ToNot(HaveOccurred())
451 newVMI := &v12.VirtualMachineInstance{}
452 err = json.Unmarshal(tpl.Bytes(), newVMI)
453 Expect(err).ToNot(HaveOccurred())
454 Expect(newVMI).To(Equal(exampleVMI))
455 })
456 It("Marshal struct into json", func() {
457 exampleVMI.Spec.Domain.Devices.Interfaces = []v12.Interface{
458 v12.Interface{
459 Name: "default",
460 InterfaceBindingMethod: v12.InterfaceBindingMethod{
461 DeprecatedSlirp: &v12.DeprecatedInterfaceSlirp{},
462 },
463 },
464 }
465
466 networkTemplateData := NetworkTemplateConfig{InterfaceConfig: `"slirp": {}`}
467 tmpl, err := template.New("vmexample").Parse(exampleJSON)
468 Expect(err).ToNot(HaveOccurred())
469 var tpl bytes.Buffer
470 err = tmpl.Execute(&tpl, networkTemplateData)
471 Expect(err).ToNot(HaveOccurred())
472 exampleJSONParsed := tpl.String()
473 buf, err := json.MarshalIndent(*exampleVMI, "", " ")
474 Expect(err).ToNot(HaveOccurred())
475 Expect(string(buf)).To(Equal(exampleJSONParsed))
476 })
477 It("Marshal struct into json with port configure", func() {
478 exampleVMI.Spec.Domain.Devices.Interfaces = []v12.Interface{
479 v12.Interface{
480 Name: "default",
481 InterfaceBindingMethod: v12.InterfaceBindingMethod{
482 DeprecatedSlirp: &v12.DeprecatedInterfaceSlirp{}},
483 Ports: []v12.Port{{Port: 80}},
484 },
485 }
486 networkTemplateData := NetworkTemplateConfig{InterfaceConfig: `"slirp": {},
487 "ports": [
488 {
489 "port": 80
490 }
491 ]`}
492
493 tmpl, err := template.New("vmexample").Parse(exampleJSON)
494 Expect(err).ToNot(HaveOccurred())
495 var tpl bytes.Buffer
496 err = tmpl.Execute(&tpl, networkTemplateData)
497 Expect(err).ToNot(HaveOccurred())
498 exampleJSONParsed := tpl.String()
499 buf, err := json.MarshalIndent(*exampleVMI, "", " ")
500 Expect(err).ToNot(HaveOccurred())
501 Expect(string(buf)).To(Equal(exampleJSONParsed))
502 })
503 })
504 })
505
View as plain text