krkn-lib 5.1.11__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- krkn_lib/aws_tests/__init__.py +1 -1
- krkn_lib/elastic/krkn_elastic.py +3 -1
- krkn_lib/k8s/krkn_kubernetes.py +408 -20
- krkn_lib/k8s/pod_monitor/__init__.py +1 -2
- krkn_lib/k8s/pod_monitor/pod_monitor.py +146 -56
- krkn_lib/k8s/templates/snapshot.j2 +10 -0
- krkn_lib/models/elastic/models.py +24 -1
- krkn_lib/models/k8s/models.py +1 -1
- krkn_lib/models/pod_monitor/models.py +2 -2
- krkn_lib/models/telemetry/models.py +9 -0
- krkn_lib/ocp/krkn_openshift.py +4 -4
- krkn_lib/prometheus/krkn_prometheus.py +1 -1
- krkn_lib/telemetry/k8s/krkn_telemetry_kubernetes.py +1 -1
- krkn_lib/telemetry/ocp/krkn_telemetry_openshift.py +1 -1
- krkn_lib/tests/base_test.py +16 -3
- krkn_lib/tests/test_krkn_elastic_models.py +23 -4
- krkn_lib/tests/test_krkn_kubernetes_check.py +3 -2
- krkn_lib/tests/test_krkn_kubernetes_create.py +5 -3
- krkn_lib/tests/test_krkn_kubernetes_delete.py +3 -2
- krkn_lib/tests/test_krkn_kubernetes_get.py +5 -4
- krkn_lib/tests/test_krkn_kubernetes_misc.py +3 -3
- krkn_lib/tests/test_krkn_kubernetes_models.py +1 -1
- krkn_lib/tests/test_krkn_kubernetes_pods_monitor_models.py +3 -4
- krkn_lib/tests/test_krkn_kubernetes_virt.py +735 -0
- krkn_lib/tests/test_krkn_openshift.py +571 -48
- krkn_lib/tests/test_krkn_telemetry_kubernetes.py +848 -0
- krkn_lib/tests/test_safe_logger.py +496 -0
- krkn_lib/tests/test_utils.py +4 -5
- krkn_lib/utils/functions.py +4 -3
- krkn_lib/version/version.py +5 -2
- {krkn_lib-5.1.11.dist-info → krkn_lib-6.0.0.dist-info}/METADATA +5 -8
- {krkn_lib-5.1.11.dist-info → krkn_lib-6.0.0.dist-info}/RECORD +34 -30
- {krkn_lib-5.1.11.dist-info → krkn_lib-6.0.0.dist-info}/WHEEL +1 -1
- {krkn_lib-5.1.11.dist-info/licenses → krkn_lib-6.0.0.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Test suite for KubeVirt/virtualization-related functionality in KrknKubernetes
|
|
6
|
+
|
|
7
|
+
This test suite covers KubeVirt-specific methods in the KrknKubernetes class,
|
|
8
|
+
including operations on VirtualMachines (VMs), VirtualMachineInstances (VMIs),
|
|
9
|
+
and VirtualMachineSnapshots.
|
|
10
|
+
|
|
11
|
+
Tested functionality:
|
|
12
|
+
- VM operations: get, list, delete, patch
|
|
13
|
+
- VMI operations: get, list, create, delete, patch
|
|
14
|
+
- Snapshot operations: get, create, delete
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
# Run all tests in this file
|
|
18
|
+
python -m unittest src.krkn_lib.tests.test_krkn_kubernetes_virt -v
|
|
19
|
+
|
|
20
|
+
# Run a specific test class
|
|
21
|
+
python -m unittest src.krkn_lib.tests.test_krkn_kubernetes_virt.TestKrknKubernetesVirt -v
|
|
22
|
+
|
|
23
|
+
# Run a specific test method
|
|
24
|
+
python -m unittest src.krkn_lib.tests.test_krkn_kubernetes_virt.TestKrknKubernetesVirt.test_get_vm_success -v
|
|
25
|
+
|
|
26
|
+
# Run with coverage
|
|
27
|
+
python -m coverage run -a -m unittest src.krkn_lib.tests.test_krkn_kubernetes_virt -v
|
|
28
|
+
|
|
29
|
+
Assisted By: Claude Code
|
|
30
|
+
""" # NOQA
|
|
31
|
+
|
|
32
|
+
import unittest
|
|
33
|
+
from unittest.mock import MagicMock, PropertyMock, patch
|
|
34
|
+
|
|
35
|
+
from kubernetes.client.rest import ApiException
|
|
36
|
+
|
|
37
|
+
from krkn_lib.k8s.krkn_kubernetes import KrknKubernetes
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestKrknKubernetesVirt(unittest.TestCase):
|
|
41
|
+
|
|
42
|
+
def setUp(self):
|
|
43
|
+
"""Set up mock objects for each test"""
|
|
44
|
+
# Create a mock for custom_object_client
|
|
45
|
+
self.mock_custom_client = MagicMock()
|
|
46
|
+
|
|
47
|
+
# Import and create instance with all mocks in place
|
|
48
|
+
with (
|
|
49
|
+
patch("kubernetes.config.load_kube_config"),
|
|
50
|
+
patch("kubernetes.client.CoreV1Api"),
|
|
51
|
+
patch("kubernetes.client.CustomObjectsApi"),
|
|
52
|
+
patch("kubernetes.client.AppsV1Api"),
|
|
53
|
+
patch("kubernetes.client.BatchV1Api"),
|
|
54
|
+
patch("kubernetes.client.ApiClient"),
|
|
55
|
+
patch("kubernetes.client.VersionApi"),
|
|
56
|
+
patch("kubernetes.client.NetworkingV1Api"),
|
|
57
|
+
):
|
|
58
|
+
# Create KrknKubernetes instance with mocked dependencies
|
|
59
|
+
self.lib_k8s = KrknKubernetes(kubeconfig_path="dummy")
|
|
60
|
+
|
|
61
|
+
# Patch the custom_object_client property to return our mock
|
|
62
|
+
self.custom_client_patcher = patch.object(
|
|
63
|
+
type(self.lib_k8s),
|
|
64
|
+
"custom_object_client",
|
|
65
|
+
new_callable=PropertyMock,
|
|
66
|
+
return_value=self.mock_custom_client,
|
|
67
|
+
)
|
|
68
|
+
self.custom_client_patcher.start()
|
|
69
|
+
|
|
70
|
+
# Mock list_namespaces_by_regex method
|
|
71
|
+
self.list_namespaces_patcher = patch.object(
|
|
72
|
+
self.lib_k8s,
|
|
73
|
+
"list_namespaces_by_regex",
|
|
74
|
+
return_value=[],
|
|
75
|
+
)
|
|
76
|
+
self.list_namespaces_patcher.start()
|
|
77
|
+
|
|
78
|
+
def tearDown(self):
|
|
79
|
+
"""Clean up patches after each test"""
|
|
80
|
+
self.custom_client_patcher.stop()
|
|
81
|
+
self.list_namespaces_patcher.stop()
|
|
82
|
+
|
|
83
|
+
def test_get_vm_success(self):
|
|
84
|
+
"""Test get_vm returns VM when it exists"""
|
|
85
|
+
vm_name = "test-vm"
|
|
86
|
+
namespace = "test-ns"
|
|
87
|
+
expected_vm = {
|
|
88
|
+
"metadata": {"name": vm_name, "namespace": namespace},
|
|
89
|
+
"spec": {"running": True},
|
|
90
|
+
}
|
|
91
|
+
# Configure the mock to return expected_vm
|
|
92
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
93
|
+
mock_get.return_value = expected_vm
|
|
94
|
+
|
|
95
|
+
result = self.lib_k8s.get_vm(vm_name, namespace)
|
|
96
|
+
|
|
97
|
+
mock_get.assert_called_once_with(
|
|
98
|
+
group="kubevirt.io",
|
|
99
|
+
version="v1",
|
|
100
|
+
namespace=namespace,
|
|
101
|
+
plural="virtualmachines",
|
|
102
|
+
name=vm_name,
|
|
103
|
+
)
|
|
104
|
+
self.assertEqual(result, expected_vm)
|
|
105
|
+
|
|
106
|
+
def test_get_vm_not_found(self):
|
|
107
|
+
"""Test get_vm returns None when VM doesn't exist"""
|
|
108
|
+
vm_name = "non-existent-vm"
|
|
109
|
+
namespace = "test-ns"
|
|
110
|
+
api_exception = ApiException(status=404)
|
|
111
|
+
|
|
112
|
+
# Configure the mock to raise 404
|
|
113
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
114
|
+
mock_get.side_effect = api_exception
|
|
115
|
+
|
|
116
|
+
result = self.lib_k8s.get_vm(vm_name, namespace)
|
|
117
|
+
self.assertIsNone(result)
|
|
118
|
+
|
|
119
|
+
def test_get_vm_api_error(self):
|
|
120
|
+
"""Test get_vm raises exception on API error"""
|
|
121
|
+
vm_name = "test-vm"
|
|
122
|
+
namespace = "test-ns"
|
|
123
|
+
api_exception = ApiException(status=500)
|
|
124
|
+
|
|
125
|
+
# Configure the mock to raise 500
|
|
126
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
127
|
+
mock_get.side_effect = api_exception
|
|
128
|
+
|
|
129
|
+
with self.assertRaises(ApiException):
|
|
130
|
+
self.lib_k8s.get_vm(vm_name, namespace)
|
|
131
|
+
|
|
132
|
+
def test_get_vmi_success(self):
|
|
133
|
+
"""Test get_vmi returns VMI when it exists"""
|
|
134
|
+
vmi_name = "test-vmi"
|
|
135
|
+
namespace = "test-ns"
|
|
136
|
+
expected_vmi = {
|
|
137
|
+
"metadata": {"name": vmi_name, "namespace": namespace},
|
|
138
|
+
"status": {"phase": "Running"},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# Configure the mock to return expected_vmi
|
|
142
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
143
|
+
mock_get.return_value = expected_vmi
|
|
144
|
+
|
|
145
|
+
result = self.lib_k8s.get_vmi(vmi_name, namespace)
|
|
146
|
+
|
|
147
|
+
mock_get.assert_called_once_with(
|
|
148
|
+
group="kubevirt.io",
|
|
149
|
+
version="v1",
|
|
150
|
+
namespace=namespace,
|
|
151
|
+
plural="virtualmachineinstances",
|
|
152
|
+
name=vmi_name,
|
|
153
|
+
)
|
|
154
|
+
self.assertEqual(result, expected_vmi)
|
|
155
|
+
|
|
156
|
+
def test_get_vmi_not_found(self):
|
|
157
|
+
"""Test get_vmi returns None when VMI doesn't exist"""
|
|
158
|
+
vmi_name = "non-existent-vmi"
|
|
159
|
+
namespace = "test-ns"
|
|
160
|
+
api_exception = ApiException(status=404)
|
|
161
|
+
|
|
162
|
+
# Configure the mock to raise 404
|
|
163
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
164
|
+
mock_get.side_effect = api_exception
|
|
165
|
+
|
|
166
|
+
result = self.lib_k8s.get_vmi(vmi_name, namespace)
|
|
167
|
+
self.assertIsNone(result)
|
|
168
|
+
|
|
169
|
+
def test_get_vmi_api_error(self):
|
|
170
|
+
"""Test get_vmi raises exception on API error"""
|
|
171
|
+
vmi_name = "test-vmi"
|
|
172
|
+
namespace = "test-ns"
|
|
173
|
+
api_exception = ApiException(status=500)
|
|
174
|
+
|
|
175
|
+
# Configure the mock to raise 500
|
|
176
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
177
|
+
mock_get.side_effect = api_exception
|
|
178
|
+
|
|
179
|
+
with self.assertRaises(ApiException):
|
|
180
|
+
self.lib_k8s.get_vmi(vmi_name, namespace)
|
|
181
|
+
|
|
182
|
+
def test_get_vmis_success(self):
|
|
183
|
+
"""Test get_vmis returns matching VMIs"""
|
|
184
|
+
regex_name = "^test-vmi-.*"
|
|
185
|
+
namespace = "test-ns"
|
|
186
|
+
vmi1 = {
|
|
187
|
+
"metadata": {"name": "test-vmi-1"},
|
|
188
|
+
"status": {"phase": "Running"},
|
|
189
|
+
}
|
|
190
|
+
vmi2 = {
|
|
191
|
+
"metadata": {"name": "test-vmi-2"},
|
|
192
|
+
"status": {"phase": "Running"},
|
|
193
|
+
}
|
|
194
|
+
vmi3 = {
|
|
195
|
+
"metadata": {"name": "other-vmi"},
|
|
196
|
+
"status": {"phase": "Running"},
|
|
197
|
+
}
|
|
198
|
+
vmis_response = {"items": [vmi1, vmi2, vmi3]}
|
|
199
|
+
|
|
200
|
+
# Mock list_namespaces_by_regex
|
|
201
|
+
with patch.object(
|
|
202
|
+
self.lib_k8s,
|
|
203
|
+
"list_namespaces_by_regex",
|
|
204
|
+
return_value=[namespace],
|
|
205
|
+
):
|
|
206
|
+
# Configure the mock to return vmis
|
|
207
|
+
mock_list = self.mock_custom_client.list_namespaced_custom_object
|
|
208
|
+
mock_list.return_value = vmis_response
|
|
209
|
+
|
|
210
|
+
# get_vmis returns a list
|
|
211
|
+
result = self.lib_k8s.get_vmis(regex_name, namespace)
|
|
212
|
+
|
|
213
|
+
# Check that only matching VMIs were returned
|
|
214
|
+
self.assertEqual(len(result), 2)
|
|
215
|
+
self.assertIn(vmi1, result)
|
|
216
|
+
self.assertIn(vmi2, result)
|
|
217
|
+
self.assertNotIn(vmi3, result)
|
|
218
|
+
|
|
219
|
+
def test_get_vmis_not_found(self):
|
|
220
|
+
"""Test get_vmis handles 404 gracefully"""
|
|
221
|
+
regex_name = "^test-vmi-.*"
|
|
222
|
+
namespace = "test-ns"
|
|
223
|
+
api_exception = ApiException(status=404)
|
|
224
|
+
|
|
225
|
+
# Mock list_namespaces_by_regex
|
|
226
|
+
with patch.object(
|
|
227
|
+
self.lib_k8s,
|
|
228
|
+
"list_namespaces_by_regex",
|
|
229
|
+
return_value=[namespace],
|
|
230
|
+
):
|
|
231
|
+
# Configure the mock to raise 404
|
|
232
|
+
mock_list = self.mock_custom_client.list_namespaced_custom_object
|
|
233
|
+
mock_list.side_effect = api_exception
|
|
234
|
+
|
|
235
|
+
result = self.lib_k8s.get_vmis(regex_name, namespace)
|
|
236
|
+
# Returns [] when 404
|
|
237
|
+
self.assertEqual(result, [])
|
|
238
|
+
|
|
239
|
+
def test_get_vmis_multiple_namespaces(self):
|
|
240
|
+
"""Test get_vmis searches across multiple namespaces"""
|
|
241
|
+
regex_name = "^test-vmi-.*"
|
|
242
|
+
namespace_pattern = "test-ns-.*"
|
|
243
|
+
namespaces = ["test-ns-1", "test-ns-2"]
|
|
244
|
+
vmi1 = {"metadata": {"name": "test-vmi-1"}}
|
|
245
|
+
vmi2 = {"metadata": {"name": "test-vmi-2"}}
|
|
246
|
+
vmis_response_1 = {"items": [vmi1]}
|
|
247
|
+
vmis_response_2 = {"items": [vmi2]}
|
|
248
|
+
|
|
249
|
+
# Mock list_namespaces_by_regex
|
|
250
|
+
with patch.object(
|
|
251
|
+
self.lib_k8s,
|
|
252
|
+
"list_namespaces_by_regex",
|
|
253
|
+
return_value=namespaces,
|
|
254
|
+
):
|
|
255
|
+
# Configure mock for different responses
|
|
256
|
+
mock_list = self.mock_custom_client.list_namespaced_custom_object
|
|
257
|
+
mock_list.side_effect = [
|
|
258
|
+
vmis_response_1,
|
|
259
|
+
vmis_response_2,
|
|
260
|
+
]
|
|
261
|
+
|
|
262
|
+
# get_vmis returns a list
|
|
263
|
+
result = self.lib_k8s.get_vmis(regex_name, namespace_pattern)
|
|
264
|
+
|
|
265
|
+
self.assertEqual(len(result), 2)
|
|
266
|
+
self.assertIn(vmi1, result)
|
|
267
|
+
self.assertIn(vmi2, result)
|
|
268
|
+
|
|
269
|
+
def test_get_vms_success(self):
|
|
270
|
+
"""Test get_vms returns matching VMs"""
|
|
271
|
+
regex_name = "^test-vm-.*"
|
|
272
|
+
namespace = "test-ns"
|
|
273
|
+
vm1 = {
|
|
274
|
+
"metadata": {"name": "test-vm-1"},
|
|
275
|
+
"spec": {"running": True},
|
|
276
|
+
}
|
|
277
|
+
vm2 = {
|
|
278
|
+
"metadata": {"name": "test-vm-2"},
|
|
279
|
+
"spec": {"running": False},
|
|
280
|
+
}
|
|
281
|
+
vm3 = {
|
|
282
|
+
"metadata": {"name": "other-vm"},
|
|
283
|
+
"spec": {"running": True},
|
|
284
|
+
}
|
|
285
|
+
vms_response = {"items": [vm1, vm2, vm3]}
|
|
286
|
+
|
|
287
|
+
# Mock list_namespaces_by_regex
|
|
288
|
+
with patch.object(
|
|
289
|
+
self.lib_k8s,
|
|
290
|
+
"list_namespaces_by_regex",
|
|
291
|
+
return_value=[namespace],
|
|
292
|
+
):
|
|
293
|
+
# Configure the mock to return vms
|
|
294
|
+
mock_list = self.mock_custom_client.list_namespaced_custom_object
|
|
295
|
+
mock_list.return_value = vms_response
|
|
296
|
+
|
|
297
|
+
result = self.lib_k8s.get_vms(regex_name, namespace)
|
|
298
|
+
|
|
299
|
+
self.assertEqual(len(result), 2)
|
|
300
|
+
self.assertIn(vm1, result)
|
|
301
|
+
self.assertIn(vm2, result)
|
|
302
|
+
self.assertNotIn(vm3, result)
|
|
303
|
+
|
|
304
|
+
def test_get_vms_not_found(self):
|
|
305
|
+
"""Test get_vms returns empty list when no VMs found"""
|
|
306
|
+
regex_name = "^test-vm-.*"
|
|
307
|
+
namespace = "test-ns"
|
|
308
|
+
api_exception = ApiException(status=404)
|
|
309
|
+
|
|
310
|
+
# Mock list_namespaces_by_regex
|
|
311
|
+
with patch.object(
|
|
312
|
+
self.lib_k8s,
|
|
313
|
+
"list_namespaces_by_regex",
|
|
314
|
+
return_value=[namespace],
|
|
315
|
+
):
|
|
316
|
+
# Configure the mock to raise 404
|
|
317
|
+
mock_list = self.mock_custom_client.list_namespaced_custom_object
|
|
318
|
+
mock_list.side_effect = api_exception
|
|
319
|
+
|
|
320
|
+
result = self.lib_k8s.get_vms(regex_name, namespace)
|
|
321
|
+
self.assertEqual(result, [])
|
|
322
|
+
|
|
323
|
+
def test_get_vms_multiple_namespaces(self):
|
|
324
|
+
"""Test get_vms searches across multiple namespaces"""
|
|
325
|
+
regex_name = "^test-vm-.*"
|
|
326
|
+
namespace_pattern = "test-ns-.*"
|
|
327
|
+
namespaces = ["test-ns-1", "test-ns-2"]
|
|
328
|
+
vm1 = {"metadata": {"name": "test-vm-1"}}
|
|
329
|
+
vm2 = {"metadata": {"name": "test-vm-2"}}
|
|
330
|
+
vms_response_1 = {"items": [vm1]}
|
|
331
|
+
vms_response_2 = {"items": [vm2]}
|
|
332
|
+
|
|
333
|
+
# Mock list_namespaces_by_regex
|
|
334
|
+
with patch.object(
|
|
335
|
+
self.lib_k8s,
|
|
336
|
+
"list_namespaces_by_regex",
|
|
337
|
+
return_value=namespaces,
|
|
338
|
+
):
|
|
339
|
+
# Configure mock for different responses
|
|
340
|
+
mock_list = self.mock_custom_client.list_namespaced_custom_object
|
|
341
|
+
mock_list.side_effect = [
|
|
342
|
+
vms_response_1,
|
|
343
|
+
vms_response_2,
|
|
344
|
+
]
|
|
345
|
+
|
|
346
|
+
result = self.lib_k8s.get_vms(regex_name, namespace_pattern)
|
|
347
|
+
|
|
348
|
+
self.assertEqual(len(result), 2)
|
|
349
|
+
self.assertIn(vm1, result)
|
|
350
|
+
self.assertIn(vm2, result)
|
|
351
|
+
|
|
352
|
+
def test_delete_vm_success(self):
|
|
353
|
+
"""Test delete_vm successfully deletes a VM"""
|
|
354
|
+
vm_name = "test-vm"
|
|
355
|
+
namespace = "test-ns"
|
|
356
|
+
expected_response = {
|
|
357
|
+
"metadata": {"name": vm_name},
|
|
358
|
+
"status": {"phase": "Terminating"},
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
# Configure the mock to return expected response
|
|
362
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
363
|
+
mock_delete.return_value = expected_response
|
|
364
|
+
|
|
365
|
+
result = self.lib_k8s.delete_vm(vm_name, namespace)
|
|
366
|
+
|
|
367
|
+
mock_delete.assert_called_once_with(
|
|
368
|
+
group="kubevirt.io",
|
|
369
|
+
version="v1",
|
|
370
|
+
namespace=namespace,
|
|
371
|
+
plural="virtualmachines",
|
|
372
|
+
name=vm_name,
|
|
373
|
+
)
|
|
374
|
+
self.assertEqual(result, expected_response)
|
|
375
|
+
|
|
376
|
+
def test_delete_vm_not_found(self):
|
|
377
|
+
"""Test delete_vm returns None when VM doesn't exist"""
|
|
378
|
+
vm_name = "non-existent-vm"
|
|
379
|
+
namespace = "test-ns"
|
|
380
|
+
api_exception = ApiException(status=404)
|
|
381
|
+
|
|
382
|
+
# Configure the mock to raise 404
|
|
383
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
384
|
+
mock_delete.side_effect = api_exception
|
|
385
|
+
|
|
386
|
+
result = self.lib_k8s.delete_vm(vm_name, namespace)
|
|
387
|
+
self.assertIsNone(result)
|
|
388
|
+
|
|
389
|
+
def test_delete_vm_api_error(self):
|
|
390
|
+
"""Test delete_vm raises exception on API error"""
|
|
391
|
+
vm_name = "test-vm"
|
|
392
|
+
namespace = "test-ns"
|
|
393
|
+
api_exception = ApiException(status=500)
|
|
394
|
+
|
|
395
|
+
# Configure the mock to raise 500
|
|
396
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
397
|
+
mock_delete.side_effect = api_exception
|
|
398
|
+
|
|
399
|
+
with self.assertRaises(ApiException):
|
|
400
|
+
self.lib_k8s.delete_vm(vm_name, namespace)
|
|
401
|
+
|
|
402
|
+
def test_delete_vmi_success(self):
|
|
403
|
+
"""Test delete_vmi successfully deletes a VMI"""
|
|
404
|
+
vmi_name = "test-vmi"
|
|
405
|
+
namespace = "test-ns"
|
|
406
|
+
|
|
407
|
+
# Configure the mock to return None (success)
|
|
408
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
409
|
+
mock_delete.return_value = None
|
|
410
|
+
|
|
411
|
+
result = self.lib_k8s.delete_vmi(vmi_name, namespace)
|
|
412
|
+
|
|
413
|
+
mock_delete.assert_called_once_with(
|
|
414
|
+
group="kubevirt.io",
|
|
415
|
+
version="v1",
|
|
416
|
+
namespace=namespace,
|
|
417
|
+
plural="virtualmachineinstances",
|
|
418
|
+
name=vmi_name,
|
|
419
|
+
)
|
|
420
|
+
# delete_vmi doesn't explicitly return on success (returns None)
|
|
421
|
+
self.assertIsNone(result)
|
|
422
|
+
|
|
423
|
+
def test_delete_vmi_not_found(self):
|
|
424
|
+
"""Test delete_vmi returns 1 when VMI doesn't exist"""
|
|
425
|
+
vmi_name = "non-existent-vmi"
|
|
426
|
+
namespace = "test-ns"
|
|
427
|
+
api_exception = ApiException(status=404)
|
|
428
|
+
|
|
429
|
+
# Configure the mock to raise 404
|
|
430
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
431
|
+
mock_delete.side_effect = api_exception
|
|
432
|
+
|
|
433
|
+
result = self.lib_k8s.delete_vmi(vmi_name, namespace)
|
|
434
|
+
# Returns 1 on 404
|
|
435
|
+
self.assertEqual(result, 1)
|
|
436
|
+
|
|
437
|
+
def test_delete_vmi_api_error(self):
|
|
438
|
+
"""Test delete_vmi returns 1 on API error"""
|
|
439
|
+
vmi_name = "test-vmi"
|
|
440
|
+
namespace = "test-ns"
|
|
441
|
+
api_exception = ApiException(status=500)
|
|
442
|
+
|
|
443
|
+
# Configure the mock to raise 500
|
|
444
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
445
|
+
mock_delete.side_effect = api_exception
|
|
446
|
+
|
|
447
|
+
result = self.lib_k8s.delete_vmi(vmi_name, namespace)
|
|
448
|
+
# Returns 1 on error
|
|
449
|
+
self.assertEqual(result, 1)
|
|
450
|
+
|
|
451
|
+
def test_get_snapshot_success(self):
|
|
452
|
+
"""Test get_snapshot returns snapshot when it exists"""
|
|
453
|
+
snapshot_name = "test-snapshot"
|
|
454
|
+
namespace = "test-ns"
|
|
455
|
+
expected_snapshot = {
|
|
456
|
+
"metadata": {
|
|
457
|
+
"name": snapshot_name,
|
|
458
|
+
"namespace": namespace,
|
|
459
|
+
},
|
|
460
|
+
"spec": {"source": {"name": "test-vm"}},
|
|
461
|
+
"status": {"readyToUse": True},
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
# Configure the mock to return expected_snapshot
|
|
465
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
466
|
+
mock_get.return_value = expected_snapshot
|
|
467
|
+
|
|
468
|
+
result = self.lib_k8s.get_snapshot(snapshot_name, namespace)
|
|
469
|
+
|
|
470
|
+
mock_get.assert_called_once_with(
|
|
471
|
+
group="snapshot.kubevirt.io",
|
|
472
|
+
version="v1beta1",
|
|
473
|
+
namespace=namespace,
|
|
474
|
+
plural="virtualmachinesnapshots",
|
|
475
|
+
name=snapshot_name,
|
|
476
|
+
)
|
|
477
|
+
self.assertEqual(result, expected_snapshot)
|
|
478
|
+
|
|
479
|
+
def test_get_snapshot_not_found(self):
|
|
480
|
+
"""Test get_snapshot returns None when not found"""
|
|
481
|
+
snapshot_name = "non-existent-snapshot"
|
|
482
|
+
namespace = "test-ns"
|
|
483
|
+
api_exception = ApiException(status=404)
|
|
484
|
+
|
|
485
|
+
# Configure the mock to raise 404
|
|
486
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
487
|
+
mock_get.side_effect = api_exception
|
|
488
|
+
|
|
489
|
+
result = self.lib_k8s.get_snapshot(snapshot_name, namespace)
|
|
490
|
+
self.assertIsNone(result)
|
|
491
|
+
|
|
492
|
+
def test_get_snapshot_api_error(self):
|
|
493
|
+
"""Test get_snapshot raises exception on API error"""
|
|
494
|
+
snapshot_name = "test-snapshot"
|
|
495
|
+
namespace = "test-ns"
|
|
496
|
+
api_exception = ApiException(status=500)
|
|
497
|
+
|
|
498
|
+
# Configure the mock to raise 500
|
|
499
|
+
mock_get = self.mock_custom_client.get_namespaced_custom_object
|
|
500
|
+
mock_get.side_effect = api_exception
|
|
501
|
+
|
|
502
|
+
with self.assertRaises(ApiException):
|
|
503
|
+
self.lib_k8s.get_snapshot(snapshot_name, namespace)
|
|
504
|
+
|
|
505
|
+
def test_delete_snapshot_success(self):
|
|
506
|
+
"""Test delete_snapshot successfully deletes a snapshot"""
|
|
507
|
+
snapshot_name = "test-snapshot"
|
|
508
|
+
namespace = "test-ns"
|
|
509
|
+
|
|
510
|
+
# Configure the mock to return None (success)
|
|
511
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
512
|
+
mock_delete.return_value = None
|
|
513
|
+
|
|
514
|
+
# Should not raise any exception
|
|
515
|
+
self.lib_k8s.delete_snapshot(snapshot_name, namespace)
|
|
516
|
+
|
|
517
|
+
mock_delete.assert_called_once_with(
|
|
518
|
+
group="snapshot.kubevirt.io",
|
|
519
|
+
version="v1beta1",
|
|
520
|
+
namespace=namespace,
|
|
521
|
+
plural="virtualmachinesnapshots",
|
|
522
|
+
name=snapshot_name,
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
def test_delete_snapshot_not_found(self):
|
|
526
|
+
"""Test delete_snapshot handles deletion gracefully"""
|
|
527
|
+
snapshot_name = "non-existent-snapshot"
|
|
528
|
+
namespace = "test-ns"
|
|
529
|
+
api_exception = ApiException(status=404)
|
|
530
|
+
|
|
531
|
+
# Configure the mock to raise 404
|
|
532
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
533
|
+
mock_delete.side_effect = api_exception
|
|
534
|
+
|
|
535
|
+
# Should not raise exception
|
|
536
|
+
self.lib_k8s.delete_snapshot(snapshot_name, namespace)
|
|
537
|
+
|
|
538
|
+
def test_delete_snapshot_api_error(self):
|
|
539
|
+
"""Test delete_snapshot handles API errors gracefully"""
|
|
540
|
+
snapshot_name = "test-snapshot"
|
|
541
|
+
namespace = "test-ns"
|
|
542
|
+
api_exception = ApiException(status=500)
|
|
543
|
+
|
|
544
|
+
# Configure the mock to raise 500
|
|
545
|
+
mock_delete = self.mock_custom_client.delete_namespaced_custom_object
|
|
546
|
+
mock_delete.side_effect = api_exception
|
|
547
|
+
|
|
548
|
+
# Should not raise exception
|
|
549
|
+
self.lib_k8s.delete_snapshot(snapshot_name, namespace)
|
|
550
|
+
|
|
551
|
+
def test_create_vmi_success(self):
|
|
552
|
+
"""Test create_vmi successfully creates a VMI"""
|
|
553
|
+
vmi_name = "test-vmi"
|
|
554
|
+
namespace = "test-ns"
|
|
555
|
+
vm_name = "test-vm"
|
|
556
|
+
vmi_body = {
|
|
557
|
+
"apiVersion": "kubevirt.io/v1",
|
|
558
|
+
"kind": "VirtualMachineInstance",
|
|
559
|
+
"metadata": {"name": vmi_name, "namespace": namespace},
|
|
560
|
+
"spec": {"domain": {"devices": {}}},
|
|
561
|
+
}
|
|
562
|
+
expected_vmi = {
|
|
563
|
+
"metadata": {"name": vmi_name, "namespace": namespace},
|
|
564
|
+
"status": {"phase": "Pending"},
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# Configure the mock to return expected_vmi
|
|
568
|
+
mock_create = self.mock_custom_client.create_namespaced_custom_object
|
|
569
|
+
mock_create.return_value = expected_vmi
|
|
570
|
+
|
|
571
|
+
result = self.lib_k8s.create_vmi(
|
|
572
|
+
vmi_name, namespace, vm_name, vmi_body
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
mock_create.assert_called_once_with(
|
|
576
|
+
group="kubevirt.io",
|
|
577
|
+
version="v1",
|
|
578
|
+
namespace=namespace,
|
|
579
|
+
plural="virtualmachineinstances",
|
|
580
|
+
body=vmi_body,
|
|
581
|
+
)
|
|
582
|
+
self.assertEqual(result, expected_vmi)
|
|
583
|
+
|
|
584
|
+
def test_create_vmi_not_found(self):
|
|
585
|
+
"""Test create_vmi returns None when resource not found"""
|
|
586
|
+
vmi_name = "test-vmi"
|
|
587
|
+
namespace = "test-ns"
|
|
588
|
+
vm_name = "non-existent-vm"
|
|
589
|
+
vmi_body = {"metadata": {"name": vmi_name}}
|
|
590
|
+
api_exception = ApiException(status=404)
|
|
591
|
+
|
|
592
|
+
# Configure the mock to raise 404
|
|
593
|
+
mock_create = self.mock_custom_client.create_namespaced_custom_object
|
|
594
|
+
mock_create.side_effect = api_exception
|
|
595
|
+
|
|
596
|
+
result = self.lib_k8s.create_vmi(
|
|
597
|
+
vmi_name, namespace, vm_name, vmi_body
|
|
598
|
+
)
|
|
599
|
+
self.assertIsNone(result)
|
|
600
|
+
|
|
601
|
+
def test_create_vmi_api_error(self):
|
|
602
|
+
"""Test create_vmi raises exception on API error"""
|
|
603
|
+
vmi_name = "test-vmi"
|
|
604
|
+
namespace = "test-ns"
|
|
605
|
+
vm_name = "test-vm"
|
|
606
|
+
vmi_body = {"metadata": {"name": vmi_name}}
|
|
607
|
+
api_exception = ApiException(status=500)
|
|
608
|
+
|
|
609
|
+
# Configure the mock to raise 500
|
|
610
|
+
mock_create = self.mock_custom_client.create_namespaced_custom_object
|
|
611
|
+
mock_create.side_effect = api_exception
|
|
612
|
+
|
|
613
|
+
with self.assertRaises(ApiException):
|
|
614
|
+
self.lib_k8s.create_vmi(vmi_name, namespace, vm_name, vmi_body)
|
|
615
|
+
|
|
616
|
+
def test_patch_vm_success(self):
|
|
617
|
+
"""Test patch_vm successfully patches a VM"""
|
|
618
|
+
vm_name = "test-vm"
|
|
619
|
+
namespace = "test-ns"
|
|
620
|
+
vm_body = {
|
|
621
|
+
"spec": {
|
|
622
|
+
"running": True,
|
|
623
|
+
"template": {"metadata": {"labels": {"app": "test"}}},
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
expected_vm = {
|
|
627
|
+
"metadata": {"name": vm_name, "namespace": namespace},
|
|
628
|
+
"spec": {"running": True},
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
# Configure the mock to return expected_vm
|
|
632
|
+
mock_patch = self.mock_custom_client.patch_namespaced_custom_object
|
|
633
|
+
mock_patch.return_value = expected_vm
|
|
634
|
+
|
|
635
|
+
result = self.lib_k8s.patch_vm(vm_name, namespace, vm_body)
|
|
636
|
+
|
|
637
|
+
mock_patch.assert_called_once_with(
|
|
638
|
+
group="kubevirt.io",
|
|
639
|
+
version="v1",
|
|
640
|
+
namespace=namespace,
|
|
641
|
+
plural="virtualmachines",
|
|
642
|
+
name=vm_name,
|
|
643
|
+
body=vm_body,
|
|
644
|
+
)
|
|
645
|
+
self.assertEqual(result, expected_vm)
|
|
646
|
+
|
|
647
|
+
def test_patch_vm_not_found(self):
|
|
648
|
+
"""Test patch_vm returns None when VM doesn't exist"""
|
|
649
|
+
vm_name = "non-existent-vm"
|
|
650
|
+
namespace = "test-ns"
|
|
651
|
+
vm_body = {"spec": {"running": False}}
|
|
652
|
+
api_exception = ApiException(status=404)
|
|
653
|
+
|
|
654
|
+
# Configure the mock to raise 404
|
|
655
|
+
mock_patch = self.mock_custom_client.patch_namespaced_custom_object
|
|
656
|
+
mock_patch.side_effect = api_exception
|
|
657
|
+
|
|
658
|
+
result = self.lib_k8s.patch_vm(vm_name, namespace, vm_body)
|
|
659
|
+
self.assertIsNone(result)
|
|
660
|
+
|
|
661
|
+
def test_patch_vm_api_error(self):
|
|
662
|
+
"""Test patch_vm raises exception on API error"""
|
|
663
|
+
vm_name = "test-vm"
|
|
664
|
+
namespace = "test-ns"
|
|
665
|
+
vm_body = {"spec": {"running": True}}
|
|
666
|
+
api_exception = ApiException(status=500)
|
|
667
|
+
|
|
668
|
+
# Configure the mock to raise 500
|
|
669
|
+
mock_patch = self.mock_custom_client.patch_namespaced_custom_object
|
|
670
|
+
mock_patch.side_effect = api_exception
|
|
671
|
+
|
|
672
|
+
with self.assertRaises(ApiException):
|
|
673
|
+
self.lib_k8s.patch_vm(vm_name, namespace, vm_body)
|
|
674
|
+
|
|
675
|
+
def test_patch_vmi_success(self):
|
|
676
|
+
"""Test patch_vmi successfully patches a VMI"""
|
|
677
|
+
vmi_name = "test-vmi"
|
|
678
|
+
namespace = "test-ns"
|
|
679
|
+
vmi_body = {
|
|
680
|
+
"metadata": {
|
|
681
|
+
"labels": {"environment": "production"},
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
expected_vmi = {
|
|
685
|
+
"metadata": {
|
|
686
|
+
"name": vmi_name,
|
|
687
|
+
"namespace": namespace,
|
|
688
|
+
"labels": {"environment": "production"},
|
|
689
|
+
},
|
|
690
|
+
"status": {"phase": "Running"},
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
# Configure the mock to return expected_vmi
|
|
694
|
+
mock_patch = self.mock_custom_client.patch_namespaced_custom_object
|
|
695
|
+
mock_patch.return_value = expected_vmi
|
|
696
|
+
|
|
697
|
+
result = self.lib_k8s.patch_vmi(vmi_name, namespace, vmi_body)
|
|
698
|
+
|
|
699
|
+
mock_patch.assert_called_once_with(
|
|
700
|
+
group="kubevirt.io",
|
|
701
|
+
version="v1",
|
|
702
|
+
namespace=namespace,
|
|
703
|
+
plural="virtualmachineinstances",
|
|
704
|
+
name=vmi_name,
|
|
705
|
+
body=vmi_body,
|
|
706
|
+
)
|
|
707
|
+
self.assertEqual(result, expected_vmi)
|
|
708
|
+
|
|
709
|
+
def test_patch_vmi_not_found(self):
|
|
710
|
+
"""Test patch_vmi returns None when VMI doesn't exist"""
|
|
711
|
+
vmi_name = "non-existent-vmi"
|
|
712
|
+
namespace = "test-ns"
|
|
713
|
+
vmi_body = {"metadata": {"labels": {"app": "test"}}}
|
|
714
|
+
api_exception = ApiException(status=404)
|
|
715
|
+
|
|
716
|
+
# Configure the mock to raise 404
|
|
717
|
+
mock_patch = self.mock_custom_client.patch_namespaced_custom_object
|
|
718
|
+
mock_patch.side_effect = api_exception
|
|
719
|
+
|
|
720
|
+
result = self.lib_k8s.patch_vmi(vmi_name, namespace, vmi_body)
|
|
721
|
+
self.assertIsNone(result)
|
|
722
|
+
|
|
723
|
+
def test_patch_vmi_api_error(self):
|
|
724
|
+
"""Test patch_vmi raises exception on API error"""
|
|
725
|
+
vmi_name = "test-vmi"
|
|
726
|
+
namespace = "test-ns"
|
|
727
|
+
vmi_body = {"metadata": {"labels": {"app": "test"}}}
|
|
728
|
+
api_exception = ApiException(status=500)
|
|
729
|
+
|
|
730
|
+
# Configure the mock to raise 500
|
|
731
|
+
mock_patch = self.mock_custom_client.patch_namespaced_custom_object
|
|
732
|
+
mock_patch.side_effect = api_exception
|
|
733
|
+
|
|
734
|
+
with self.assertRaises(ApiException):
|
|
735
|
+
self.lib_k8s.patch_vmi(vmi_name, namespace, vmi_body)
|