Pin black version in tox.ini to 23.12.1
[osm/N2VC.git] / n2vc / tests / unit / test_libjuju.py
1 # Copyright 2020 Canonical Ltd.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import asyncio
16 import asynctest
17 import tempfile
18 from unittest.mock import Mock, patch
19 import juju
20 import kubernetes
21 from juju.errors import JujuAPIError
22 import logging
23
24 from n2vc.definitions import Offer, RelationEndpoint
25 from .utils import (
26 FakeApplication,
27 FakeMachine,
28 FakeManualMachine,
29 FakeUnit,
30 )
31 from n2vc.libjuju import Libjuju
32 from n2vc.exceptions import (
33 JujuControllerFailedConnecting,
34 JujuMachineNotFound,
35 JujuApplicationNotFound,
36 JujuActionNotFound,
37 JujuApplicationExists,
38 JujuInvalidK8sConfiguration,
39 JujuLeaderUnitNotFound,
40 JujuError,
41 )
42 from n2vc.k8s_juju_conn import generate_rbac_id
43 from n2vc.tests.unit.utils import AsyncMock
44 from n2vc.vca.connection import Connection
45 from n2vc.vca.connection_data import ConnectionData
46
47
48 cacert = """-----BEGIN CERTIFICATE-----
49 SOMECERT
50 -----END CERTIFICATE-----"""
51
52
53 @asynctest.mock.patch("n2vc.libjuju.Controller")
54 class LibjujuTestCase(asynctest.TestCase):
55 @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
56 def setUp(
57 self,
58 mock_base64_to_cacert=None,
59 ):
60 self.loop = asyncio.get_event_loop()
61 self.db = Mock()
62 mock_base64_to_cacert.return_value = cacert
63 # Connection._load_vca_connection_data = Mock()
64 vca_connection = Connection(AsyncMock())
65 vca_connection._data = ConnectionData(
66 **{
67 "endpoints": ["1.2.3.4:17070"],
68 "user": "user",
69 "secret": "secret",
70 "cacert": "cacert",
71 "pubkey": "pubkey",
72 "lxd-cloud": "cloud",
73 "lxd-credentials": "credentials",
74 "k8s-cloud": "k8s_cloud",
75 "k8s-credentials": "k8s_credentials",
76 "model-config": {},
77 "api-proxy": "api_proxy",
78 }
79 )
80 logging.disable(logging.CRITICAL)
81 self.libjuju = Libjuju(vca_connection, self.loop)
82 self.loop.run_until_complete(self.libjuju.disconnect())
83
84
85 @asynctest.mock.patch("juju.controller.Controller.connect")
86 @asynctest.mock.patch(
87 "juju.controller.Controller.api_endpoints",
88 new_callable=asynctest.CoroutineMock(return_value=["127.0.0.1:17070"]),
89 )
90 class GetControllerTest(LibjujuTestCase):
91 def setUp(self):
92 super(GetControllerTest, self).setUp()
93
94 def test_diff_endpoint(self, mock_api_endpoints, mock_connect):
95 self.libjuju.endpoints = []
96 controller = self.loop.run_until_complete(self.libjuju.get_controller())
97 self.assertIsInstance(controller, juju.controller.Controller)
98
99 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
100 def test_exception(
101 self,
102 mock_disconnect_controller,
103 mock_api_endpoints,
104 mock_connect,
105 ):
106 self.libjuju.endpoints = []
107
108 mock_connect.side_effect = Exception()
109 controller = None
110 with self.assertRaises(JujuControllerFailedConnecting):
111 controller = self.loop.run_until_complete(self.libjuju.get_controller())
112 self.assertIsNone(controller)
113 mock_disconnect_controller.assert_called()
114
115 def test_same_endpoint_get_controller(self, mock_api_endpoints, mock_connect):
116 self.libjuju.endpoints = ["127.0.0.1:17070"]
117 controller = self.loop.run_until_complete(self.libjuju.get_controller())
118 self.assertIsInstance(controller, juju.controller.Controller)
119
120
121 class DisconnectTest(LibjujuTestCase):
122 def setUp(self):
123 super(DisconnectTest, self).setUp()
124
125 @asynctest.mock.patch("juju.model.Model.disconnect")
126 def test_disconnect_model(self, mock_disconnect):
127 self.loop.run_until_complete(self.libjuju.disconnect_model(juju.model.Model()))
128 mock_disconnect.assert_called_once()
129
130 @asynctest.mock.patch("juju.controller.Controller.disconnect")
131 def test_disconnect_controller(self, mock_disconnect):
132 self.loop.run_until_complete(
133 self.libjuju.disconnect_controller(juju.controller.Controller())
134 )
135 mock_disconnect.assert_called_once()
136
137
138 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
139 @asynctest.mock.patch("n2vc.libjuju.Libjuju.model_exists")
140 @asynctest.mock.patch("juju.controller.Controller.add_model")
141 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
142 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
143 class AddModelTest(LibjujuTestCase):
144 def setUp(self):
145 super(AddModelTest, self).setUp()
146
147 def test_existing_model(
148 self,
149 mock_disconnect_model,
150 mock_disconnect_controller,
151 mock_add_model,
152 mock_model_exists,
153 mock_get_controller,
154 ):
155 mock_model_exists.return_value = True
156
157 # This should not raise an exception
158 self.loop.run_until_complete(self.libjuju.add_model("existing_model", "cloud"))
159
160 mock_disconnect_controller.assert_called()
161
162 # TODO Check two job executing at the same time and one returning without doing anything.
163
164 def test_non_existing_model(
165 self,
166 mock_disconnect_model,
167 mock_disconnect_controller,
168 mock_add_model,
169 mock_model_exists,
170 mock_get_controller,
171 ):
172 mock_model_exists.return_value = False
173 mock_get_controller.return_value = juju.controller.Controller()
174
175 self.loop.run_until_complete(
176 self.libjuju.add_model("nonexisting_model", Mock())
177 )
178
179 mock_add_model.assert_called_once()
180 mock_disconnect_controller.assert_called()
181 mock_disconnect_model.assert_called()
182
183
184 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
185 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
186 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
187 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
188 @asynctest.mock.patch(
189 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
190 )
191 @asynctest.mock.patch("juju.model.Model.get_action_status")
192 @asynctest.mock.patch("juju.model.Model.get_action_output")
193 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_actions")
194 class GetExecutedActionsTest(LibjujuTestCase):
195 def setUp(self):
196 super(GetExecutedActionsTest, self).setUp()
197
198 def test_exception(
199 self,
200 mock_get_actions,
201 mock_get_action_output,
202 mock_get_action_status,
203 mock_applications,
204 mock_disconnect_controller,
205 mock_disconnect_model,
206 mock_get_model,
207 mock_get_controller,
208 ):
209 mock_get_model.return_value = None
210 with self.assertRaises(JujuError):
211 self.loop.run_until_complete(self.libjuju.get_executed_actions("model"))
212
213 mock_get_controller.assert_called_once()
214 mock_disconnect_controller.assert_called_once()
215 mock_get_model.assert_called_once()
216 mock_disconnect_model.assert_not_called()
217
218 def test_success(
219 self,
220 mock_get_actions,
221 mock_get_action_output,
222 mock_get_action_status,
223 mock_applications,
224 mock_disconnect_controller,
225 mock_disconnect_model,
226 mock_get_model,
227 mock_get_controller,
228 ):
229 mock_get_model.return_value = juju.model.Model()
230 mock_applications.return_value = {"existing_app"}
231 mock_get_actions.return_value = {"action_name": "description"}
232 mock_get_action_status.return_value = {"id": "status"}
233 mock_get_action_output.return_value = {"output": "completed"}
234
235 executed_actions = self.loop.run_until_complete(
236 self.libjuju.get_executed_actions("model")
237 )
238 expected_result = [
239 {
240 "id": "id",
241 "action": "action_name",
242 "status": "status",
243 "output": "completed",
244 }
245 ]
246 self.assertListEqual(expected_result, executed_actions)
247 self.assertIsInstance(executed_actions, list)
248
249 mock_get_controller.assert_called_once()
250 mock_get_model.assert_called_once()
251 mock_disconnect_controller.assert_called_once()
252 mock_disconnect_model.assert_called_once()
253
254
255 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
256 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
257 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
258 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
259 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
260 class GetApplicationConfigsTest(LibjujuTestCase):
261 def setUp(self):
262 super(GetApplicationConfigsTest, self).setUp()
263
264 def test_exception(
265 self,
266 mock_get_application,
267 mock_disconnect_controller,
268 mock_disconnect_model,
269 mock_get_model,
270 mock_get_controller,
271 ):
272 mock_get_model.return_value = None
273 with self.assertRaises(JujuError):
274 self.loop.run_until_complete(
275 self.libjuju.get_application_configs("model", "app")
276 )
277
278 mock_get_controller.assert_called_once()
279 mock_disconnect_controller.assert_called_once()
280 mock_get_model.assert_called_once()
281 mock_disconnect_model.assert_not_called()
282
283 def test_success(
284 self,
285 mock_get_application,
286 mock_disconnect_controller,
287 mock_disconnect_model,
288 mock_get_model,
289 mock_get_controller,
290 ):
291 mock_get_application.return_value = FakeApplication()
292 application_configs = self.loop.run_until_complete(
293 self.libjuju.get_application_configs("model", "app")
294 )
295
296 self.assertEqual(application_configs, ["app_config"])
297
298 mock_get_controller.assert_called_once()
299 mock_get_model.assert_called_once()
300 mock_disconnect_controller.assert_called_once()
301 mock_disconnect_model.assert_called_once()
302
303
304 @asynctest.mock.patch("juju.controller.Controller.get_model")
305 class GetModelTest(LibjujuTestCase):
306 def setUp(self):
307 super(GetModelTest, self).setUp()
308
309 def test_get_model(
310 self,
311 mock_get_model,
312 ):
313 mock_get_model.return_value = juju.model.Model()
314 model = self.loop.run_until_complete(
315 self.libjuju.get_model(juju.controller.Controller(), "model")
316 )
317 self.assertIsInstance(model, juju.model.Model)
318
319
320 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
321 @asynctest.mock.patch("juju.controller.Controller.list_models")
322 class ModelExistsTest(LibjujuTestCase):
323 def setUp(self):
324 super(ModelExistsTest, self).setUp()
325
326 async def test_existing_model(
327 self,
328 mock_list_models,
329 mock_get_controller,
330 ):
331 mock_list_models.return_value = ["existing_model"]
332 self.assertTrue(
333 await self.libjuju.model_exists(
334 "existing_model", juju.controller.Controller()
335 )
336 )
337
338 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
339 async def test_no_controller(
340 self,
341 mock_disconnect_controller,
342 mock_list_models,
343 mock_get_controller,
344 ):
345 mock_list_models.return_value = ["existing_model"]
346 mock_get_controller.return_value = juju.controller.Controller()
347 self.assertTrue(await self.libjuju.model_exists("existing_model"))
348 mock_disconnect_controller.assert_called_once()
349
350 async def test_non_existing_model(
351 self,
352 mock_list_models,
353 mock_get_controller,
354 ):
355 mock_list_models.return_value = ["existing_model"]
356 self.assertFalse(
357 await self.libjuju.model_exists(
358 "not_existing_model", juju.controller.Controller()
359 )
360 )
361
362
363 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
364 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
365 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
366 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
367 @asynctest.mock.patch("juju.model.Model.get_status")
368 class GetModelStatusTest(LibjujuTestCase):
369 def setUp(self):
370 super(GetModelStatusTest, self).setUp()
371
372 def test_success(
373 self,
374 mock_get_status,
375 mock_disconnect_controller,
376 mock_disconnect_model,
377 mock_get_model,
378 mock_get_controller,
379 ):
380 mock_get_model.return_value = juju.model.Model()
381 mock_get_status.return_value = {"status"}
382
383 status = self.loop.run_until_complete(self.libjuju.get_model_status("model"))
384
385 mock_get_status.assert_called_once()
386 mock_disconnect_controller.assert_called_once()
387 mock_disconnect_model.assert_called_once()
388
389 self.assertEqual(status, {"status"})
390
391 def test_exception(
392 self,
393 mock_get_status,
394 mock_disconnect_controller,
395 mock_disconnect_model,
396 mock_get_model,
397 mock_get_controller,
398 ):
399 mock_get_model.return_value = juju.model.Model()
400 mock_get_status.side_effect = Exception()
401 status = None
402 with self.assertRaises(Exception):
403 status = self.loop.run_until_complete(
404 self.libjuju.get_model_status("model")
405 )
406
407 mock_disconnect_controller.assert_called_once()
408 mock_disconnect_model.assert_called_once()
409
410 self.assertIsNone(status)
411
412
413 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
414 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
415 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
416 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
417 @asynctest.mock.patch("juju.model.Model.get_machines")
418 @asynctest.mock.patch("juju.model.Model.add_machine")
419 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
420 class CreateMachineTest(LibjujuTestCase):
421 def setUp(self):
422 super(CreateMachineTest, self).setUp()
423
424 def test_existing_machine(
425 self,
426 mock_wait_for,
427 mock_add_machine,
428 mock_get_machines,
429 mock_disconnect_controller,
430 mock_disconnect_model,
431 mock_get_model,
432 mock_get_controller,
433 ):
434 mock_get_model.return_value = juju.model.Model()
435 mock_get_machines.return_value = {"existing_machine": FakeMachine()}
436 machine, bool_res = self.loop.run_until_complete(
437 self.libjuju.create_machine("model", "existing_machine")
438 )
439
440 self.assertIsInstance(machine, FakeMachine)
441 self.assertFalse(bool_res)
442
443 mock_disconnect_controller.assert_called()
444 mock_disconnect_model.assert_called()
445
446 def test_non_existing_machine(
447 self,
448 mock_wait_for,
449 mock_add_machine,
450 mock_get_machines,
451 mock_disconnect_controller,
452 mock_disconnect_model,
453 mock_get_model,
454 mock_get_controller,
455 ):
456 machine = None
457 bool_res = None
458 mock_get_model.return_value = juju.model.Model()
459 with self.assertRaises(JujuMachineNotFound):
460 machine, bool_res = self.loop.run_until_complete(
461 self.libjuju.create_machine("model", "non_existing_machine")
462 )
463 self.assertIsNone(machine)
464 self.assertIsNone(bool_res)
465
466 mock_disconnect_controller.assert_called()
467 mock_disconnect_model.assert_called()
468
469 def test_no_machine(
470 self,
471 mock_wait_for,
472 mock_add_machine,
473 mock_get_machines,
474 mock_disconnect_controller,
475 mock_disconnect_model,
476 mock_get_model,
477 mock_get_controller,
478 ):
479 mock_get_model.return_value = juju.model.Model()
480 mock_add_machine.return_value = FakeMachine()
481
482 machine, bool_res = self.loop.run_until_complete(
483 self.libjuju.create_machine("model")
484 )
485
486 self.assertIsInstance(machine, FakeMachine)
487 self.assertTrue(bool_res)
488
489 mock_wait_for.assert_called_once()
490 mock_add_machine.assert_called_once()
491
492 mock_disconnect_controller.assert_called()
493 mock_disconnect_model.assert_called()
494
495
496 # TODO test provision machine
497
498
499 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
500 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
501 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
502 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
503 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for_model")
504 @asynctest.mock.patch("juju.model.Model.deploy")
505 class DeployTest(LibjujuTestCase):
506 def setUp(self):
507 super(DeployTest, self).setUp()
508
509 def test_deploy(
510 self,
511 mock_deploy,
512 mock_wait_for_model,
513 mock_disconnect_controller,
514 mock_disconnect_model,
515 mock_get_model,
516 mock_get_controller,
517 ):
518 mock_get_model.return_value = juju.model.Model()
519 self.loop.run_until_complete(
520 self.libjuju.deploy("cs:osm", "model", wait=True, timeout=0)
521 )
522 mock_deploy.assert_called_once()
523 mock_wait_for_model.assert_called_once()
524 mock_disconnect_controller.assert_called_once()
525 mock_disconnect_model.assert_called_once()
526
527 def test_deploy_no_wait(
528 self,
529 mock_deploy,
530 mock_wait_for_model,
531 mock_disconnect_controller,
532 mock_disconnect_model,
533 mock_get_model,
534 mock_get_controller,
535 ):
536 mock_get_model.return_value = juju.model.Model()
537 self.loop.run_until_complete(
538 self.libjuju.deploy("cs:osm", "model", wait=False, timeout=0)
539 )
540 mock_deploy.assert_called_once()
541 mock_wait_for_model.assert_not_called()
542 mock_disconnect_controller.assert_called_once()
543 mock_disconnect_model.assert_called_once()
544
545 def test_deploy_exception(
546 self,
547 mock_deploy,
548 mock_wait_for_model,
549 mock_disconnect_controller,
550 mock_disconnect_model,
551 mock_get_model,
552 mock_get_controller,
553 ):
554 mock_deploy.side_effect = Exception()
555 mock_get_model.return_value = juju.model.Model()
556 with self.assertRaises(Exception):
557 self.loop.run_until_complete(self.libjuju.deploy("cs:osm", "model"))
558 mock_deploy.assert_called_once()
559 mock_wait_for_model.assert_not_called()
560 mock_disconnect_controller.assert_called_once()
561 mock_disconnect_model.assert_called_once()
562
563
564 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
565 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
566 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
567 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
568 @asynctest.mock.patch(
569 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
570 )
571 @asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
572 @asynctest.mock.patch("juju.model.Model.deploy")
573 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
574 @asynctest.mock.patch("n2vc.libjuju.Libjuju.create_machine")
575 class DeployCharmTest(LibjujuTestCase):
576 def setUp(self):
577 super(DeployCharmTest, self).setUp()
578
579 def test_existing_app(
580 self,
581 mock_create_machine,
582 mock_wait_for,
583 mock_deploy,
584 mock_machines,
585 mock_applications,
586 mock_disconnect_controller,
587 mock_disconnect_model,
588 mock_get_model,
589 mock_get_controller,
590 ):
591 mock_get_model.return_value = juju.model.Model()
592 mock_applications.return_value = {"existing_app"}
593
594 application = None
595 with self.assertRaises(JujuApplicationExists):
596 application = self.loop.run_until_complete(
597 self.libjuju.deploy_charm(
598 "existing_app",
599 "path",
600 "model",
601 "machine",
602 )
603 )
604 self.assertIsNone(application)
605
606 mock_disconnect_controller.assert_called()
607 mock_disconnect_model.assert_called()
608
609 def test_non_existing_machine(
610 self,
611 mock_create_machine,
612 mock_wait_for,
613 mock_deploy,
614 mock_machines,
615 mock_applications,
616 mock_disconnect_controller,
617 mock_disconnect_model,
618 mock_get_model,
619 mock_get_controller,
620 ):
621 mock_get_model.return_value = juju.model.Model()
622 mock_machines.return_value = {"existing_machine": FakeMachine()}
623 application = None
624 with self.assertRaises(JujuMachineNotFound):
625 application = self.loop.run_until_complete(
626 self.libjuju.deploy_charm(
627 "app",
628 "path",
629 "model",
630 "machine",
631 )
632 )
633
634 self.assertIsNone(application)
635
636 mock_disconnect_controller.assert_called()
637 mock_disconnect_model.assert_called()
638
639 def test_2_units(
640 self,
641 mock_create_machine,
642 mock_wait_for,
643 mock_deploy,
644 mock_machines,
645 mock_applications,
646 mock_disconnect_controller,
647 mock_disconnect_model,
648 mock_get_model,
649 mock_get_controller,
650 ):
651 mock_get_model.return_value = juju.model.Model()
652 mock_machines.return_value = {"existing_machine": FakeMachine()}
653 mock_create_machine.return_value = (FakeMachine(), "other")
654 mock_deploy.return_value = FakeApplication()
655 application = self.loop.run_until_complete(
656 self.libjuju.deploy_charm(
657 "app",
658 "path",
659 "model",
660 "existing_machine",
661 num_units=2,
662 )
663 )
664
665 self.assertIsInstance(application, FakeApplication)
666
667 mock_deploy.assert_called_once()
668 mock_wait_for.assert_called_once()
669
670 mock_create_machine.assert_called_once()
671
672 mock_disconnect_controller.assert_called()
673 mock_disconnect_model.assert_called()
674
675 def test_1_unit(
676 self,
677 mock_create_machine,
678 mock_wait_for,
679 mock_deploy,
680 mock_machines,
681 mock_applications,
682 mock_disconnect_controller,
683 mock_disconnect_model,
684 mock_get_model,
685 mock_get_controller,
686 ):
687 mock_get_model.return_value = juju.model.Model()
688 mock_machines.return_value = {"existing_machine": FakeMachine()}
689 mock_deploy.return_value = FakeApplication()
690 application = self.loop.run_until_complete(
691 self.libjuju.deploy_charm("app", "path", "model", "existing_machine")
692 )
693
694 self.assertIsInstance(application, FakeApplication)
695
696 mock_deploy.assert_called_once()
697 mock_wait_for.assert_called_once()
698
699 mock_disconnect_controller.assert_called()
700 mock_disconnect_model.assert_called()
701
702
703 @asynctest.mock.patch(
704 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
705 )
706 class GetApplicationTest(LibjujuTestCase):
707 def setUp(self):
708 super(GetApplicationTest, self).setUp()
709
710 def test_existing_application(
711 self,
712 mock_applications,
713 ):
714 mock_applications.return_value = {"existing_app": "exists"}
715 model = juju.model.Model()
716 result = self.libjuju._get_application(model, "existing_app")
717 self.assertEqual(result, "exists")
718
719 def test_non_existing_application(
720 self,
721 mock_applications,
722 ):
723 mock_applications.return_value = {"existing_app": "exists"}
724 model = juju.model.Model()
725 result = self.libjuju._get_application(model, "nonexisting_app")
726 self.assertIsNone(result)
727
728
729 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
730 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
731 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
732 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
733 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
734 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
735 @asynctest.mock.patch("juju.model.Model.get_action_output")
736 @asynctest.mock.patch("juju.model.Model.get_action_status")
737 class ExecuteActionTest(LibjujuTestCase):
738 def setUp(self):
739 super(ExecuteActionTest, self).setUp()
740
741 def test_no_application(
742 self,
743 mock_get_action_status,
744 mock_get_action_output,
745 mock_wait_for,
746 mock__get_application,
747 mock_disconnect_controller,
748 mock_disconnect_model,
749 mock_get_model,
750 mock_get_controller,
751 ):
752 mock__get_application.return_value = None
753 mock_get_model.return_value = juju.model.Model()
754 output = None
755 status = None
756 with self.assertRaises(JujuApplicationNotFound):
757 output, status = self.loop.run_until_complete(
758 self.libjuju.execute_action(
759 "app",
760 "model",
761 "action",
762 )
763 )
764 self.assertIsNone(output)
765 self.assertIsNone(status)
766
767 mock_disconnect_controller.assert_called()
768 mock_disconnect_model.assert_called()
769
770 def test_no_action(
771 self,
772 mock_get_action_status,
773 mock_get_action_output,
774 mock_wait_for,
775 mock__get_application,
776 mock_disconnect_controller,
777 mock_disconnect_model,
778 mock_get_model,
779 mock_get_controller,
780 ):
781 mock_get_model.return_value = juju.model.Model()
782 mock__get_application.return_value = FakeApplication()
783 output = None
784 status = None
785 with self.assertRaises(JujuActionNotFound):
786 output, status = self.loop.run_until_complete(
787 self.libjuju.execute_action(
788 "app",
789 "model",
790 "action",
791 )
792 )
793 self.assertIsNone(output)
794 self.assertIsNone(status)
795
796 mock_disconnect_controller.assert_called()
797 mock_disconnect_model.assert_called()
798
799 @asynctest.mock.patch("asyncio.sleep")
800 @asynctest.mock.patch("n2vc.tests.unit.utils.FakeUnit.is_leader_from_status")
801 def test_no_leader(
802 self,
803 mock_is_leader_from_status,
804 mock_sleep,
805 mock_get_action_status,
806 mock_get_action_output,
807 mock_wait_for,
808 mock__get_application,
809 mock_disconnect_controller,
810 mock_disconnect_model,
811 mock_get_model,
812 mock_get_controller,
813 ):
814 mock_get_model.return_value = juju.model.Model()
815 mock__get_application.return_value = FakeApplication()
816 mock_is_leader_from_status.return_value = False
817 output = None
818 status = None
819 with self.assertRaises(JujuLeaderUnitNotFound):
820 output, status = self.loop.run_until_complete(
821 self.libjuju.execute_action(
822 "app",
823 "model",
824 "action",
825 )
826 )
827 self.assertIsNone(output)
828 self.assertIsNone(status)
829
830 mock_disconnect_controller.assert_called()
831 mock_disconnect_model.assert_called()
832
833 def test_successful_exec(
834 self,
835 mock_get_action_status,
836 mock_get_action_output,
837 mock_wait_for,
838 mock__get_application,
839 mock_disconnect_controller,
840 mock_disconnect_model,
841 mock_get_model,
842 mock_get_controller,
843 ):
844 mock_get_model.return_value = juju.model.Model()
845 mock__get_application.return_value = FakeApplication()
846 mock_get_action_output.return_value = "output"
847 mock_get_action_status.return_value = {"id": "status"}
848 output, status = self.loop.run_until_complete(
849 self.libjuju.execute_action("app", "model", "existing_action")
850 )
851 self.assertEqual(output, "output")
852 self.assertEqual(status, "status")
853
854 mock_wait_for.assert_called_once()
855
856 mock_disconnect_controller.assert_called()
857 mock_disconnect_model.assert_called()
858
859
860 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
861 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
862 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
863 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
864 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
865 class GetActionTest(LibjujuTestCase):
866 def setUp(self):
867 super(GetActionTest, self).setUp()
868
869 def test_exception(
870 self,
871 mock_get_application,
872 mock_disconnect_controller,
873 mock_disconnect_model,
874 mock_get_model,
875 mock_get_controller,
876 ):
877 mock_get_application.side_effect = Exception()
878 actions = None
879 with self.assertRaises(Exception):
880 actions = self.loop.run_until_complete(
881 self.libjuju.get_actions("app", "model")
882 )
883
884 self.assertIsNone(actions)
885 mock_disconnect_controller.assert_called_once()
886 mock_disconnect_model.assert_called_once()
887
888 def test_success(
889 self,
890 mock_get_application,
891 mock_disconnect_controller,
892 mock_disconnect_model,
893 mock_get_model,
894 mock_get_controller,
895 ):
896 mock_get_application.return_value = FakeApplication()
897
898 actions = self.loop.run_until_complete(self.libjuju.get_actions("app", "model"))
899
900 self.assertEqual(actions, ["existing_action"])
901
902 mock_get_controller.assert_called_once()
903 mock_get_model.assert_called_once()
904 mock_disconnect_controller.assert_called_once()
905 mock_disconnect_model.assert_called_once()
906
907
908 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
909 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
910 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
911 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
912 @asynctest.mock.patch("juju.application.Application.get_metrics")
913 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
914 class GetMetricsTest(LibjujuTestCase):
915 def setUp(self):
916 super(GetMetricsTest, self).setUp()
917
918 def test_get_metrics_success(
919 self,
920 mock_get_application,
921 mock_get_metrics,
922 mock_disconnect_controller,
923 mock_disconnect_model,
924 mock_get_model,
925 mock_get_controller,
926 ):
927 mock_get_application.return_value = FakeApplication()
928 mock_get_model.return_value = juju.model.Model()
929
930 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
931
932 mock_disconnect_controller.assert_called_once()
933 mock_disconnect_model.assert_called_once()
934
935 def test_get_metrics_exception(
936 self,
937 mock_get_application,
938 mock_get_metrics,
939 mock_disconnect_controller,
940 mock_disconnect_model,
941 mock_get_model,
942 mock_get_controller,
943 ):
944 mock_get_model.return_value = juju.model.Model()
945 mock_get_metrics.side_effect = Exception()
946 with self.assertRaises(Exception):
947 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
948
949 mock_disconnect_controller.assert_called_once()
950 mock_disconnect_model.assert_called_once()
951
952 def test_missing_args_exception(
953 self,
954 mock_get_application,
955 mock_get_metrics,
956 mock_disconnect_controller,
957 mock_disconnect_model,
958 mock_get_model,
959 mock_get_controller,
960 ):
961 mock_get_model.return_value = juju.model.Model()
962
963 with self.assertRaises(Exception):
964 self.loop.run_until_complete(self.libjuju.get_metrics("", ""))
965
966 mock_get_controller.assert_not_called()
967 mock_get_model.assert_not_called()
968 mock_disconnect_controller.assert_not_called()
969 mock_disconnect_model.assert_not_called()
970
971
972 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
973 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
974 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
975 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
976 @asynctest.mock.patch("juju.model.Model.add_relation")
977 class AddRelationTest(LibjujuTestCase):
978 def setUp(self):
979 super(AddRelationTest, self).setUp()
980
981 @asynctest.mock.patch("logging.Logger.warning")
982 def test_not_found(
983 self,
984 mock_warning,
985 mock_add_relation,
986 mock_disconnect_controller,
987 mock_disconnect_model,
988 mock_get_model,
989 mock_get_controller,
990 ):
991 # TODO in libjuju.py should this fail only with a log message?
992 result = {"error": "not found", "response": "response", "request-id": 1}
993
994 mock_get_model.return_value = juju.model.Model()
995 mock_add_relation.side_effect = JujuAPIError(result)
996
997 self.loop.run_until_complete(
998 self.libjuju.add_relation(
999 "model",
1000 "app1:relation1",
1001 "app2:relation2",
1002 )
1003 )
1004
1005 mock_warning.assert_called_with("Relation not found: not found")
1006 mock_disconnect_controller.assert_called_once()
1007 mock_disconnect_model.assert_called_once()
1008
1009 @asynctest.mock.patch("logging.Logger.warning")
1010 def test_not_found_in_error_code(
1011 self,
1012 mock_warning,
1013 mock_add_relation,
1014 mock_disconnect_controller,
1015 mock_disconnect_model,
1016 mock_get_model,
1017 mock_get_controller,
1018 ):
1019 result = {
1020 "error": "relation cannot be added",
1021 "error-code": "not found",
1022 "response": "response",
1023 "request-id": 1,
1024 }
1025
1026 mock_get_model.return_value = juju.model.Model()
1027 mock_add_relation.side_effect = JujuAPIError(result)
1028
1029 self.loop.run_until_complete(
1030 self.libjuju.add_relation(
1031 "model",
1032 "app1:relation1",
1033 "app2:relation2",
1034 )
1035 )
1036
1037 mock_warning.assert_called_with("Relation not found: relation cannot be added")
1038 mock_disconnect_controller.assert_called_once()
1039 mock_disconnect_model.assert_called_once()
1040
1041 @asynctest.mock.patch("logging.Logger.warning")
1042 def test_already_exists(
1043 self,
1044 mock_warning,
1045 mock_add_relation,
1046 mock_disconnect_controller,
1047 mock_disconnect_model,
1048 mock_get_model,
1049 mock_get_controller,
1050 ):
1051 # TODO in libjuju.py should this fail silently?
1052 result = {"error": "already exists", "response": "response", "request-id": 1}
1053
1054 mock_get_model.return_value = juju.model.Model()
1055 mock_add_relation.side_effect = JujuAPIError(result)
1056
1057 self.loop.run_until_complete(
1058 self.libjuju.add_relation(
1059 "model",
1060 "app1:relation1",
1061 "app2:relation2",
1062 )
1063 )
1064
1065 mock_warning.assert_called_with("Relation already exists: already exists")
1066 mock_disconnect_controller.assert_called_once()
1067 mock_disconnect_model.assert_called_once()
1068
1069 @asynctest.mock.patch("logging.Logger.warning")
1070 def test_already_exists_error_code(
1071 self,
1072 mock_warning,
1073 mock_add_relation,
1074 mock_disconnect_controller,
1075 mock_disconnect_model,
1076 mock_get_model,
1077 mock_get_controller,
1078 ):
1079 result = {
1080 "error": "relation cannot be added",
1081 "error-code": "already exists",
1082 "response": "response",
1083 "request-id": 1,
1084 }
1085
1086 mock_get_model.return_value = juju.model.Model()
1087 mock_add_relation.side_effect = JujuAPIError(result)
1088
1089 self.loop.run_until_complete(
1090 self.libjuju.add_relation(
1091 "model",
1092 "app1:relation1",
1093 "app2:relation2",
1094 )
1095 )
1096
1097 mock_warning.assert_called_with(
1098 "Relation already exists: relation cannot be added"
1099 )
1100 mock_disconnect_controller.assert_called_once()
1101 mock_disconnect_model.assert_called_once()
1102
1103 def test_exception(
1104 self,
1105 mock_add_relation,
1106 mock_disconnect_controller,
1107 mock_disconnect_model,
1108 mock_get_model,
1109 mock_get_controller,
1110 ):
1111 mock_get_model.return_value = juju.model.Model()
1112 result = {"error": "", "response": "response", "request-id": 1}
1113 mock_add_relation.side_effect = JujuAPIError(result)
1114
1115 with self.assertRaises(JujuAPIError):
1116 self.loop.run_until_complete(
1117 self.libjuju.add_relation(
1118 "model",
1119 "app1:relation1",
1120 "app2:relation2",
1121 )
1122 )
1123
1124 mock_disconnect_controller.assert_called_once()
1125 mock_disconnect_model.assert_called_once()
1126
1127 def test_success(
1128 self,
1129 mock_add_relation,
1130 mock_disconnect_controller,
1131 mock_disconnect_model,
1132 mock_get_model,
1133 mock_get_controller,
1134 ):
1135 mock_get_model.return_value = juju.model.Model()
1136
1137 self.loop.run_until_complete(
1138 self.libjuju.add_relation(
1139 "model",
1140 "app1:relation1",
1141 "app2:relation2",
1142 )
1143 )
1144
1145 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
1146 mock_disconnect_controller.assert_called_once()
1147 mock_disconnect_model.assert_called_once()
1148
1149 def test_saas(
1150 self,
1151 mock_add_relation,
1152 mock_disconnect_controller,
1153 mock_disconnect_model,
1154 mock_get_model,
1155 mock_get_controller,
1156 ):
1157 mock_get_model.return_value = juju.model.Model()
1158
1159 self.loop.run_until_complete(
1160 self.libjuju.add_relation(
1161 "model",
1162 "app1:relation1",
1163 "saas_name",
1164 )
1165 )
1166
1167 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
1168 mock_disconnect_controller.assert_called_once()
1169 mock_disconnect_model.assert_called_once()
1170
1171
1172 # TODO destroy_model testcase
1173
1174
1175 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1176 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1177 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1178 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1179 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1180 class DestroyApplicationTest(LibjujuTestCase):
1181 def setUp(self):
1182 super(DestroyApplicationTest, self).setUp()
1183
1184 def test_success(
1185 self,
1186 mock_get_controller,
1187 mock_get_model,
1188 mock_disconnect_controller,
1189 mock_get_application,
1190 mock_disconnect_model,
1191 ):
1192 mock_get_application.return_value = FakeApplication()
1193 mock_get_model.return_value = None
1194 self.loop.run_until_complete(
1195 self.libjuju.destroy_application(
1196 "existing_model",
1197 "existing_app",
1198 3600,
1199 )
1200 )
1201 mock_get_application.assert_called()
1202 mock_disconnect_controller.assert_called_once()
1203 mock_disconnect_model.assert_called_once()
1204
1205 def test_no_application(
1206 self,
1207 mock_get_controller,
1208 mock_get_model,
1209 mock_disconnect_controller,
1210 mock_get_application,
1211 mock_disconnect_model,
1212 ):
1213 mock_get_model.return_value = None
1214 mock_get_application.return_value = None
1215
1216 self.loop.run_until_complete(
1217 self.libjuju.destroy_application(
1218 "existing_model",
1219 "existing_app",
1220 3600,
1221 )
1222 )
1223 mock_get_application.assert_called()
1224
1225 def test_exception(
1226 self,
1227 mock_get_controller,
1228 mock_get_model,
1229 mock_disconnect_controller,
1230 mock_get_application,
1231 mock_disconnect_model,
1232 ):
1233 mock_get_application.return_value = FakeApplication
1234 mock_get_model.return_value = None
1235
1236 with self.assertRaises(Exception):
1237 self.loop.run_until_complete(
1238 self.libjuju.destroy_application(
1239 "existing_model",
1240 "existing_app",
1241 0,
1242 )
1243 )
1244 mock_get_application.assert_called_once()
1245
1246
1247 # @asynctest.mock.patch("juju.model.Model.get_machines")
1248 # @asynctest.mock.patch("logging.Logger.debug")
1249 # class DestroyMachineTest(LibjujuTestCase):
1250 # def setUp(self):
1251 # super(DestroyMachineTest, self).setUp()
1252
1253 # def test_success_manual_machine(
1254 # self, mock_debug, mock_get_machines,
1255 # ):
1256 # mock_get_machines.side_effect = [
1257 # {"machine": FakeManualMachine()},
1258 # {"machine": FakeManualMachine()},
1259 # {},
1260 # ]
1261 # self.loop.run_until_complete(
1262 # self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
1263 # )
1264 # calls = [
1265 # asynctest.call("Waiting for machine machine is destroyed"),
1266 # asynctest.call("Machine destroyed: machine"),
1267 # ]
1268 # mock_debug.assert_has_calls(calls)
1269
1270 # def test_no_machine(
1271 # self, mock_debug, mock_get_machines,
1272 # ):
1273 # mock_get_machines.return_value = {}
1274 # self.loop.run_until_complete(
1275 # self.libjuju.destroy_machine(juju.model.Model(), "machine", 2)
1276 # )
1277 # mock_debug.assert_called_with("Machine not found: machine")
1278
1279
1280 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1281 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1282 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1283 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1284 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1285 class ConfigureApplicationTest(LibjujuTestCase):
1286 def setUp(self):
1287 super(ConfigureApplicationTest, self).setUp()
1288
1289 def test_success(
1290 self,
1291 mock_get_application,
1292 mock_disconnect_controller,
1293 mock_disconnect_model,
1294 mock_get_model,
1295 mock_get_controller,
1296 ):
1297 mock_get_application.return_value = FakeApplication()
1298
1299 self.loop.run_until_complete(
1300 self.libjuju.configure_application(
1301 "model",
1302 "app",
1303 {"config"},
1304 )
1305 )
1306 mock_get_application.assert_called_once()
1307 mock_disconnect_controller.assert_called_once()
1308 mock_disconnect_model.assert_called_once()
1309
1310 def test_exception(
1311 self,
1312 mock_get_application,
1313 mock_disconnect_controller,
1314 mock_disconnect_model,
1315 mock_get_model,
1316 mock_get_controller,
1317 ):
1318 mock_get_application.side_effect = Exception()
1319
1320 with self.assertRaises(Exception):
1321 self.loop.run_until_complete(
1322 self.libjuju.configure_application(
1323 "model",
1324 "app",
1325 {"config"},
1326 )
1327 )
1328 mock_disconnect_controller.assert_called_once()
1329 mock_disconnect_model.assert_called_once()
1330
1331 def test_controller_exception(
1332 self,
1333 mock_get_application,
1334 mock_disconnect_controller,
1335 mock_disconnect_model,
1336 mock_get_model,
1337 mock_get_controller,
1338 ):
1339 result = {"error": "not found", "response": "response", "request-id": 1}
1340
1341 mock_get_controller.side_effect = JujuAPIError(result)
1342
1343 with self.assertRaises(JujuAPIError):
1344 self.loop.run_until_complete(
1345 self.libjuju.configure_application(
1346 "model",
1347 "app",
1348 {"config"},
1349 )
1350 )
1351 mock_get_model.assert_not_called()
1352 mock_disconnect_controller.assert_not_called()
1353 mock_disconnect_model.assert_not_called()
1354
1355 def test_get_model_exception(
1356 self,
1357 mock_get_application,
1358 mock_disconnect_controller,
1359 mock_disconnect_model,
1360 mock_get_model,
1361 mock_get_controller,
1362 ):
1363 result = {"error": "not found", "response": "response", "request-id": 1}
1364 mock_get_model.side_effect = JujuAPIError(result)
1365
1366 with self.assertRaises(JujuAPIError):
1367 self.loop.run_until_complete(
1368 self.libjuju.configure_application(
1369 "model",
1370 "app",
1371 {"config"},
1372 )
1373 )
1374 mock_get_model.assert_called_once()
1375 mock_disconnect_controller.assert_called_once()
1376 mock_disconnect_model.assert_not_called()
1377
1378
1379 # TODO _get_api_endpoints_db test case
1380 # TODO _update_api_endpoints_db test case
1381 # TODO healthcheck test case
1382
1383
1384 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1385 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1386 @asynctest.mock.patch("juju.controller.Controller.list_models")
1387 class ListModelsTest(LibjujuTestCase):
1388 def setUp(self):
1389 super(ListModelsTest, self).setUp()
1390
1391 def test_containing(
1392 self,
1393 mock_list_models,
1394 mock_disconnect_controller,
1395 mock_get_controller,
1396 ):
1397 mock_get_controller.return_value = juju.controller.Controller()
1398 mock_list_models.return_value = ["existingmodel"]
1399 models = self.loop.run_until_complete(self.libjuju.list_models("existing"))
1400
1401 mock_disconnect_controller.assert_called_once()
1402 self.assertEquals(models, ["existingmodel"])
1403
1404 def test_not_containing(
1405 self,
1406 mock_list_models,
1407 mock_disconnect_controller,
1408 mock_get_controller,
1409 ):
1410 mock_get_controller.return_value = juju.controller.Controller()
1411 mock_list_models.return_value = ["existingmodel", "model"]
1412 models = self.loop.run_until_complete(self.libjuju.list_models("mdl"))
1413
1414 mock_disconnect_controller.assert_called_once()
1415 self.assertEquals(models, [])
1416
1417 def test_no_contains_arg(
1418 self,
1419 mock_list_models,
1420 mock_disconnect_controller,
1421 mock_get_controller,
1422 ):
1423 mock_get_controller.return_value = juju.controller.Controller()
1424 mock_list_models.return_value = ["existingmodel", "model"]
1425 models = self.loop.run_until_complete(self.libjuju.list_models())
1426
1427 mock_disconnect_controller.assert_called_once()
1428 self.assertEquals(models, ["existingmodel", "model"])
1429
1430
1431 @asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
1432 class ModelsExistTest(LibjujuTestCase):
1433 def setUp(self):
1434 super(ModelsExistTest, self).setUp()
1435
1436 def test_model_names_none(self, mock_list_models):
1437 mock_list_models.return_value = []
1438 with self.assertRaises(Exception):
1439 self.loop.run_until_complete(self.libjuju.models_exist(None))
1440
1441 def test_model_names_empty(self, mock_list_models):
1442 mock_list_models.return_value = []
1443 with self.assertRaises(Exception):
1444 (exist, non_existing_models) = self.loop.run_until_complete(
1445 self.libjuju.models_exist([])
1446 )
1447
1448 def test_model_names_not_existing(self, mock_list_models):
1449 mock_list_models.return_value = ["prometheus", "grafana"]
1450 (exist, non_existing_models) = self.loop.run_until_complete(
1451 self.libjuju.models_exist(["prometheus2", "grafana"])
1452 )
1453 self.assertFalse(exist)
1454 self.assertEqual(non_existing_models, ["prometheus2"])
1455
1456 def test_model_names_exist(self, mock_list_models):
1457 mock_list_models.return_value = ["prometheus", "grafana"]
1458 (exist, non_existing_models) = self.loop.run_until_complete(
1459 self.libjuju.models_exist(["prometheus", "grafana"])
1460 )
1461 self.assertTrue(exist)
1462 self.assertEqual(non_existing_models, [])
1463
1464
1465 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1466 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1467 @asynctest.mock.patch("juju.controller.Controller.list_offers")
1468 class ListOffers(LibjujuTestCase):
1469 def setUp(self):
1470 super(ListOffers, self).setUp()
1471
1472 def test_disconnect_controller(
1473 self,
1474 mock_list_offers,
1475 mock_disconnect_controller,
1476 mock_get_controller,
1477 ):
1478 mock_get_controller.return_value = juju.controller.Controller()
1479 mock_list_offers.side_effect = Exception()
1480 with self.assertRaises(Exception):
1481 self.loop.run_until_complete(self.libjuju._list_offers("model"))
1482 mock_disconnect_controller.assert_called_once()
1483
1484 def test_empty_list(
1485 self,
1486 mock_list_offers,
1487 mock_disconnect_controller,
1488 mock_get_controller,
1489 ):
1490 mock_get_controller.return_value = juju.controller.Controller()
1491 offer_results = Mock()
1492 offer_results.results = []
1493 mock_list_offers.return_value = offer_results
1494 offers = self.loop.run_until_complete(self.libjuju._list_offers("model"))
1495 self.assertEqual(offers, [])
1496 mock_disconnect_controller.assert_called_once()
1497
1498 def test_non_empty_list(
1499 self,
1500 mock_list_offers,
1501 mock_disconnect_controller,
1502 mock_get_controller,
1503 ):
1504 mock_get_controller.return_value = juju.controller.Controller()
1505 offer = Mock()
1506 offer_results = Mock()
1507 offer_results.results = [offer]
1508 mock_list_offers.return_value = offer_results
1509 offers = self.loop.run_until_complete(self.libjuju._list_offers("model"))
1510 self.assertEqual(offers, [offer])
1511 mock_disconnect_controller.assert_called_once()
1512
1513 def test_matching_offer_name(
1514 self,
1515 mock_list_offers,
1516 mock_disconnect_controller,
1517 mock_get_controller,
1518 ):
1519 mock_get_controller.return_value = juju.controller.Controller()
1520 offer_1 = Mock()
1521 offer_1.offer_name = "offer1"
1522 offer_2 = Mock()
1523 offer_2.offer_name = "offer2"
1524 offer_results = Mock()
1525 offer_results.results = [offer_1, offer_2]
1526 mock_list_offers.return_value = offer_results
1527 offers = self.loop.run_until_complete(
1528 self.libjuju._list_offers("model", offer_name="offer2")
1529 )
1530 self.assertEqual(offers, [offer_2])
1531 mock_disconnect_controller.assert_called_once()
1532
1533 def test_not_matching_offer_name(
1534 self,
1535 mock_list_offers,
1536 mock_disconnect_controller,
1537 mock_get_controller,
1538 ):
1539 mock_get_controller.return_value = juju.controller.Controller()
1540 offer_1 = Mock()
1541 offer_1.offer_name = "offer1"
1542 offer_2 = Mock()
1543 offer_2.offer_name = "offer2"
1544 offer_results = Mock()
1545 offer_results.results = [offer_1, offer_2]
1546 mock_list_offers.return_value = offer_results
1547 offers = self.loop.run_until_complete(
1548 self.libjuju._list_offers("model", offer_name="offer3")
1549 )
1550 self.assertEqual(offers, [])
1551 mock_disconnect_controller.assert_called_once()
1552
1553
1554 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1555 @asynctest.mock.patch("juju.controller.Controller.get_model")
1556 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1557 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1558 @asynctest.mock.patch("n2vc.libjuju.Libjuju._list_offers")
1559 @asynctest.mock.patch("juju.model.Model.create_offer")
1560 class OfferTest(LibjujuTestCase):
1561 def setUp(self):
1562 super(OfferTest, self).setUp()
1563
1564 def test_offer(
1565 self,
1566 mock_create_offer,
1567 mock__list_offers,
1568 mock_disconnect_controller,
1569 mock_disconnect_model,
1570 mock_get_model,
1571 mock_get_controller,
1572 ):
1573 controller = juju.controller.Controller()
1574 model = juju.model.Model()
1575 mock_get_controller.return_value = controller
1576 mock_get_model.return_value = model
1577 endpoint = RelationEndpoint("model.app-name.0", "vca", "endpoint")
1578 self.loop.run_until_complete(self.libjuju.offer(endpoint))
1579 mock_create_offer.assert_called_with(
1580 "app-name:endpoint", offer_name="app-name-endpoint"
1581 )
1582 mock_disconnect_model.assert_called_once_with(model)
1583 mock_disconnect_controller.assert_called_once_with(controller)
1584
1585 def test_offer_exception(
1586 self,
1587 mock_create_offer,
1588 mock__list_offers,
1589 mock_disconnect_controller,
1590 mock_disconnect_model,
1591 mock_get_model,
1592 mock_get_controller,
1593 ):
1594 controller = juju.controller.Controller()
1595 model = juju.model.Model()
1596 mock_get_controller.return_value = controller
1597 mock_get_model.return_value = model
1598 mock__list_offers.return_value = []
1599 endpoint = RelationEndpoint("model.app-name.0", "vca", "endpoint")
1600 with self.assertRaises(Exception):
1601 self.loop.run_until_complete(self.libjuju.offer(endpoint))
1602 mock_create_offer.assert_called_with(
1603 "app-name:endpoint", offer_name="app-name-endpoint"
1604 )
1605 mock_disconnect_model.assert_called_once_with(model)
1606 mock_disconnect_controller.assert_called_once_with(controller)
1607
1608
1609 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1610 @asynctest.mock.patch("juju.controller.Controller.get_model")
1611 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1612 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1613 @asynctest.mock.patch("juju.model.Model.consume")
1614 class ConsumeTest(LibjujuTestCase):
1615 def setUp(self):
1616 self.offer_url = "admin/model.offer_name"
1617 super(ConsumeTest, self).setUp()
1618 self.provider_libjuju = self.libjuju
1619
1620 def test_consume(
1621 self,
1622 mock_consume,
1623 mock_disconnect_controller,
1624 mock_disconnect_model,
1625 mock_get_model,
1626 mock_get_controller,
1627 ):
1628 self_controller = juju.controller.Controller()
1629 provider_controller = juju.controller.Controller()
1630 mock_get_controller.side_effect = [self_controller, provider_controller]
1631 mock_get_model.return_value = juju.model.Model()
1632
1633 self.loop.run_until_complete(
1634 self.libjuju.consume(
1635 "model_name",
1636 Offer(self.offer_url, vca_id="vca-id"),
1637 self.provider_libjuju,
1638 )
1639 )
1640 mock_consume.assert_called_once_with(
1641 "admin/model.offer_name",
1642 application_alias="offer_name-model-vca-id",
1643 controller=provider_controller,
1644 )
1645 mock_disconnect_model.assert_called_once()
1646 self.assertEqual(mock_disconnect_controller.call_count, 2)
1647
1648 def test_parsing_error_exception(
1649 self,
1650 mock_consume,
1651 mock_disconnect_controller,
1652 mock_disconnect_model,
1653 mock_get_model,
1654 mock_get_controller,
1655 ):
1656 mock_get_controller.return_value = juju.controller.Controller()
1657 mock_get_model.return_value = juju.model.Model()
1658 mock_consume.side_effect = juju.offerendpoints.ParseError("")
1659
1660 with self.assertRaises(juju.offerendpoints.ParseError):
1661 self.loop.run_until_complete(
1662 self.libjuju.consume(
1663 "model_name", Offer(self.offer_url), self.provider_libjuju
1664 )
1665 )
1666 mock_consume.assert_called_once()
1667 mock_disconnect_model.assert_called_once()
1668 self.assertEqual(mock_disconnect_controller.call_count, 2)
1669
1670 def test_juju_error_exception(
1671 self,
1672 mock_consume,
1673 mock_disconnect_controller,
1674 mock_disconnect_model,
1675 mock_get_model,
1676 mock_get_controller,
1677 ):
1678 mock_get_controller.return_value = juju.controller.Controller()
1679 mock_get_model.return_value = juju.model.Model()
1680 mock_consume.side_effect = juju.errors.JujuError("")
1681
1682 with self.assertRaises(juju.errors.JujuError):
1683 self.loop.run_until_complete(
1684 self.libjuju.consume(
1685 "model_name", Offer(self.offer_url), self.provider_libjuju
1686 )
1687 )
1688 mock_consume.assert_called_once()
1689 mock_disconnect_model.assert_called_once()
1690 self.assertEqual(mock_disconnect_controller.call_count, 2)
1691
1692 def test_juju_api_error_exception(
1693 self,
1694 mock_consume,
1695 mock_disconnect_controller,
1696 mock_disconnect_model,
1697 mock_get_model,
1698 mock_get_controller,
1699 ):
1700 mock_get_controller.return_value = juju.controller.Controller()
1701 mock_get_model.return_value = juju.model.Model()
1702 mock_consume.side_effect = juju.errors.JujuAPIError(
1703 {"error": "", "response": "", "request-id": ""}
1704 )
1705
1706 with self.assertRaises(juju.errors.JujuAPIError):
1707 self.loop.run_until_complete(
1708 self.libjuju.consume(
1709 "model_name", Offer(self.offer_url), self.provider_libjuju
1710 )
1711 )
1712 mock_consume.assert_called_once()
1713 mock_disconnect_model.assert_called_once()
1714 self.assertEqual(mock_disconnect_controller.call_count, 2)
1715
1716
1717 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_k8s_cloud_credential")
1718 @asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
1719 class AddK8sTest(LibjujuTestCase):
1720 def setUp(self):
1721 super(AddK8sTest, self).setUp()
1722 name = "cloud"
1723 rbac_id = generate_rbac_id()
1724 token = "token"
1725 client_cert_data = "cert"
1726 configuration = kubernetes.client.configuration.Configuration()
1727 storage_class = "storage_class"
1728 credential_name = name
1729
1730 self._add_k8s_args = {
1731 "name": name,
1732 "rbac_id": rbac_id,
1733 "token": token,
1734 "client_cert_data": client_cert_data,
1735 "configuration": configuration,
1736 "storage_class": storage_class,
1737 "credential_name": credential_name,
1738 }
1739
1740 def test_add_k8s(self, mock_add_cloud, mock_get_k8s_cloud_credential):
1741 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
1742 mock_add_cloud.assert_called_once()
1743 mock_get_k8s_cloud_credential.assert_called_once()
1744
1745 def test_add_k8s_exception(self, mock_add_cloud, mock_get_k8s_cloud_credential):
1746 mock_add_cloud.side_effect = Exception()
1747 with self.assertRaises(Exception):
1748 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
1749 mock_add_cloud.assert_called_once()
1750 mock_get_k8s_cloud_credential.assert_called_once()
1751
1752 def test_add_k8s_missing_name(self, mock_add_cloud, mock_get_k8s_cloud_credential):
1753 self._add_k8s_args["name"] = ""
1754 with self.assertRaises(Exception):
1755 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
1756 mock_add_cloud.assert_not_called()
1757
1758 def test_add_k8s_missing_storage_name(
1759 self, mock_add_cloud, mock_get_k8s_cloud_credential
1760 ):
1761 self._add_k8s_args["storage_class"] = ""
1762 with self.assertRaises(Exception):
1763 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
1764 mock_add_cloud.assert_not_called()
1765
1766 def test_add_k8s_missing_configuration_keys(
1767 self, mock_add_cloud, mock_get_k8s_cloud_credential
1768 ):
1769 self._add_k8s_args["configuration"] = None
1770 with self.assertRaises(Exception):
1771 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
1772 mock_add_cloud.assert_not_called()
1773
1774
1775 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1776 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1777 @asynctest.mock.patch("juju.controller.Controller.add_cloud")
1778 @asynctest.mock.patch("juju.controller.Controller.add_credential")
1779 class AddCloudTest(LibjujuTestCase):
1780 def setUp(self):
1781 super(AddCloudTest, self).setUp()
1782 self.cloud = juju.client.client.Cloud()
1783 self.credential = juju.client.client.CloudCredential()
1784
1785 def test_add_cloud_with_credential(
1786 self,
1787 mock_add_credential,
1788 mock_add_cloud,
1789 mock_disconnect_controller,
1790 mock_get_controller,
1791 ):
1792 mock_get_controller.return_value = juju.controller.Controller()
1793
1794 cloud = self.loop.run_until_complete(
1795 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1796 )
1797 self.assertEqual(cloud, self.cloud)
1798 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1799 mock_add_credential.assert_called_once_with(
1800 "cloud", credential=self.credential, cloud="cloud"
1801 )
1802 mock_disconnect_controller.assert_called_once()
1803
1804 def test_add_cloud_no_credential(
1805 self,
1806 mock_add_credential,
1807 mock_add_cloud,
1808 mock_disconnect_controller,
1809 mock_get_controller,
1810 ):
1811 mock_get_controller.return_value = juju.controller.Controller()
1812
1813 cloud = self.loop.run_until_complete(
1814 self.libjuju.add_cloud("cloud", self.cloud)
1815 )
1816 self.assertEqual(cloud, self.cloud)
1817 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1818 mock_add_credential.assert_not_called()
1819 mock_disconnect_controller.assert_called_once()
1820
1821 def test_add_cloud_exception(
1822 self,
1823 mock_add_credential,
1824 mock_add_cloud,
1825 mock_disconnect_controller,
1826 mock_get_controller,
1827 ):
1828 mock_get_controller.return_value = juju.controller.Controller()
1829 mock_add_cloud.side_effect = Exception()
1830 with self.assertRaises(Exception):
1831 self.loop.run_until_complete(
1832 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1833 )
1834
1835 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1836 mock_add_credential.assert_not_called()
1837 mock_disconnect_controller.assert_called_once()
1838
1839 def test_add_credential_exception(
1840 self,
1841 mock_add_credential,
1842 mock_add_cloud,
1843 mock_disconnect_controller,
1844 mock_get_controller,
1845 ):
1846 mock_get_controller.return_value = juju.controller.Controller()
1847 mock_add_credential.side_effect = Exception()
1848 with self.assertRaises(Exception):
1849 self.loop.run_until_complete(
1850 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1851 )
1852
1853 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1854 mock_add_credential.assert_called_once_with(
1855 "cloud", credential=self.credential, cloud="cloud"
1856 )
1857 mock_disconnect_controller.assert_called_once()
1858
1859
1860 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1861 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1862 @asynctest.mock.patch("juju.controller.Controller.remove_cloud")
1863 class RemoveCloudTest(LibjujuTestCase):
1864 def setUp(self):
1865 super(RemoveCloudTest, self).setUp()
1866
1867 def test_remove_cloud(
1868 self,
1869 mock_remove_cloud,
1870 mock_disconnect_controller,
1871 mock_get_controller,
1872 ):
1873 mock_get_controller.return_value = juju.controller.Controller()
1874
1875 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1876 mock_remove_cloud.assert_called_once_with("cloud")
1877 mock_disconnect_controller.assert_called_once()
1878
1879 def test_remove_cloud_exception(
1880 self,
1881 mock_remove_cloud,
1882 mock_disconnect_controller,
1883 mock_get_controller,
1884 ):
1885 mock_get_controller.return_value = juju.controller.Controller()
1886 mock_remove_cloud.side_effect = Exception()
1887
1888 with self.assertRaises(Exception):
1889 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1890 mock_remove_cloud.assert_called_once_with("cloud")
1891 mock_disconnect_controller.assert_called_once()
1892
1893
1894 @asynctest.mock.patch("kubernetes.client.configuration.Configuration")
1895 class GetK8sCloudCredentials(LibjujuTestCase):
1896 def setUp(self):
1897 super(GetK8sCloudCredentials, self).setUp()
1898 self.cert_data = "cert"
1899 self.token = "token"
1900
1901 @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
1902 def test_not_supported(self, mock_exception, mock_configuration):
1903 mock_configuration.username = ""
1904 mock_configuration.password = ""
1905 mock_configuration.ssl_ca_cert = None
1906 mock_configuration.cert_file = None
1907 mock_configuration.key_file = None
1908 exception_raised = False
1909 self.token = None
1910 self.cert_data = None
1911 try:
1912 _ = self.libjuju.get_k8s_cloud_credential(
1913 mock_configuration,
1914 self.cert_data,
1915 self.token,
1916 )
1917 except JujuInvalidK8sConfiguration as e:
1918 exception_raised = True
1919 self.assertEqual(
1920 e.message,
1921 "authentication method not supported",
1922 )
1923 self.assertTrue(exception_raised)
1924
1925 def test_user_pass(self, mock_configuration):
1926 mock_configuration.username = "admin"
1927 mock_configuration.password = "admin"
1928 mock_configuration.ssl_ca_cert = None
1929 mock_configuration.cert_file = None
1930 mock_configuration.key_file = None
1931 self.token = None
1932 self.cert_data = None
1933 credential = self.libjuju.get_k8s_cloud_credential(
1934 mock_configuration,
1935 self.cert_data,
1936 self.token,
1937 )
1938 self.assertEqual(
1939 credential,
1940 juju.client._definitions.CloudCredential(
1941 attrs={"username": "admin", "password": "admin"}, auth_type="userpass"
1942 ),
1943 )
1944
1945 def test_user_pass_with_cert(self, mock_configuration):
1946 mock_configuration.username = "admin"
1947 mock_configuration.password = "admin"
1948 mock_configuration.ssl_ca_cert = None
1949 mock_configuration.cert_file = None
1950 mock_configuration.key_file = None
1951 self.token = None
1952 credential = self.libjuju.get_k8s_cloud_credential(
1953 mock_configuration,
1954 self.cert_data,
1955 self.token,
1956 )
1957 self.assertEqual(
1958 credential,
1959 juju.client._definitions.CloudCredential(
1960 attrs={
1961 "ClientCertificateData": self.cert_data,
1962 "username": "admin",
1963 "password": "admin",
1964 },
1965 auth_type="userpasswithcert",
1966 ),
1967 )
1968
1969 def test_user_no_pass(self, mock_configuration):
1970 mock_configuration.username = "admin"
1971 mock_configuration.password = ""
1972 mock_configuration.ssl_ca_cert = None
1973 mock_configuration.cert_file = None
1974 mock_configuration.key_file = None
1975 self.token = None
1976 self.cert_data = None
1977 with patch.object(self.libjuju.log, "debug") as mock_debug:
1978 credential = self.libjuju.get_k8s_cloud_credential(
1979 mock_configuration,
1980 self.cert_data,
1981 self.token,
1982 )
1983 self.assertEqual(
1984 credential,
1985 juju.client._definitions.CloudCredential(
1986 attrs={"username": "admin", "password": ""}, auth_type="userpass"
1987 ),
1988 )
1989 mock_debug.assert_called_once_with(
1990 "credential for user admin has empty password"
1991 )
1992
1993 def test_cert(self, mock_configuration):
1994 mock_configuration.username = ""
1995 mock_configuration.password = ""
1996 mock_configuration.api_key = {"authorization": "Bearer Token"}
1997 ssl_ca_cert = tempfile.NamedTemporaryFile()
1998 with open(ssl_ca_cert.name, "w") as ssl_ca_cert_file:
1999 ssl_ca_cert_file.write("cacert")
2000 mock_configuration.ssl_ca_cert = ssl_ca_cert.name
2001 mock_configuration.cert_file = None
2002 mock_configuration.key_file = None
2003 credential = self.libjuju.get_k8s_cloud_credential(
2004 mock_configuration,
2005 self.cert_data,
2006 self.token,
2007 )
2008 self.assertEqual(
2009 credential,
2010 juju.client._definitions.CloudCredential(
2011 attrs={"ClientCertificateData": self.cert_data, "Token": self.token},
2012 auth_type="certificate",
2013 ),
2014 )
2015
2016 # TODO: Fix this test when oauth authentication is supported
2017 # def test_oauth2(self, mock_configuration):
2018 # mock_configuration.username = ""
2019 # mock_configuration.password = ""
2020 # mock_configuration.api_key = {"authorization": "Bearer Token"}
2021 # key = tempfile.NamedTemporaryFile()
2022 # with open(key.name, "w") as key_file:
2023 # key_file.write("key")
2024 # mock_configuration.ssl_ca_cert = None
2025 # mock_configuration.cert_file = None
2026 # mock_configuration.key_file = key.name
2027 # credential = self.libjuju.get_k8s_cloud_credential(
2028 # mock_configuration,
2029 # self.cert_data,
2030 # self.token,
2031 # )
2032 # self.assertEqual(
2033 # credential,
2034 # juju.client._definitions.CloudCredential(
2035 # attrs={"ClientKeyData": "key", "Token": "Token"},
2036 # auth_type="oauth2",
2037 # ),
2038 # )
2039
2040 # @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
2041 # def test_oauth2_missing_token(self, mock_exception, mock_configuration):
2042 # mock_configuration.username = ""
2043 # mock_configuration.password = ""
2044 # key = tempfile.NamedTemporaryFile()
2045 # with open(key.name, "w") as key_file:
2046 # key_file.write("key")
2047 # mock_configuration.ssl_ca_cert = None
2048 # mock_configuration.cert_file = None
2049 # mock_configuration.key_file = key.name
2050 # exception_raised = False
2051 # try:
2052 # _ = self.libjuju.get_k8s_cloud_credential(
2053 # mock_configuration,
2054 # self.cert_data,
2055 # self.token,
2056 # )
2057 # except JujuInvalidK8sConfiguration as e:
2058 # exception_raised = True
2059 # self.assertEqual(
2060 # e.message,
2061 # "missing token for auth type oauth2",
2062 # )
2063 # self.assertTrue(exception_raised)
2064
2065 def test_exception_cannot_set_token_and_userpass(self, mock_configuration):
2066 mock_configuration.username = "admin"
2067 mock_configuration.password = "pass"
2068 mock_configuration.api_key = {"authorization": "No_bearer_token"}
2069 mock_configuration.ssl_ca_cert = None
2070 mock_configuration.cert_file = None
2071 mock_configuration.key_file = None
2072 exception_raised = False
2073 try:
2074 _ = self.libjuju.get_k8s_cloud_credential(
2075 mock_configuration,
2076 self.cert_data,
2077 self.token,
2078 )
2079 except JujuInvalidK8sConfiguration as e:
2080 exception_raised = True
2081 self.assertEqual(
2082 e.message,
2083 "Cannot set both token and user/pass",
2084 )
2085 self.assertTrue(exception_raised)
2086
2087
2088 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2089 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2090 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2091 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2092 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2093 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for_model")
2094 class ScaleApplicationTest(LibjujuTestCase):
2095 def setUp(self):
2096 super(ScaleApplicationTest, self).setUp()
2097
2098 @asynctest.mock.patch("asyncio.sleep")
2099 def test_scale_application(
2100 self,
2101 mock_sleep,
2102 mock_wait_for_model,
2103 mock_disconnect_controller,
2104 mock_disconnect_model,
2105 mock_get_application,
2106 mock_get_model,
2107 mock_get_controller,
2108 ):
2109 mock_get_model.return_value = juju.model.Model()
2110 mock_get_application.return_value = FakeApplication()
2111 self.loop.run_until_complete(self.libjuju.scale_application("model", "app", 2))
2112 mock_wait_for_model.assert_called_once()
2113 mock_disconnect_controller.assert_called_once()
2114 mock_disconnect_model.assert_called_once()
2115
2116 def test_no_application(
2117 self,
2118 mock_wait_for,
2119 mock_disconnect_controller,
2120 mock_disconnect_model,
2121 mock_get_application,
2122 mock_get_model,
2123 mock_get_controller,
2124 ):
2125 mock_get_application.return_value = None
2126 mock_get_model.return_value = juju.model.Model()
2127 with self.assertRaises(JujuApplicationNotFound):
2128 self.loop.run_until_complete(
2129 self.libjuju.scale_application("model", "app", 2)
2130 )
2131 mock_disconnect_controller.assert_called()
2132 mock_disconnect_model.assert_called()
2133
2134 def test_exception(
2135 self,
2136 mock_wait_for,
2137 mock_disconnect_controller,
2138 mock_disconnect_model,
2139 mock_get_application,
2140 mock_get_model,
2141 mock_get_controller,
2142 ):
2143 mock_get_model.return_value = None
2144 mock_get_application.return_value = FakeApplication()
2145 with self.assertRaises(Exception):
2146 self.loop.run_until_complete(
2147 self.libjuju.scale_application("model", "app", 2, total_timeout=0)
2148 )
2149 mock_disconnect_controller.assert_called_once()
2150
2151
2152 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2153 class GetUnitNumberTest(LibjujuTestCase):
2154 def setUp(self):
2155 super(GetUnitNumberTest, self).setUp()
2156
2157 def test_successful_get_unit_number(
2158 self,
2159 mock_get_applications,
2160 ):
2161 mock_get_applications.return_value = FakeApplication()
2162 model = juju.model.Model()
2163 result = self.libjuju._get_application_count(model, "app")
2164 self.assertEqual(result, 2)
2165
2166 def test_non_existing_application(
2167 self,
2168 mock_get_applications,
2169 ):
2170 mock_get_applications.return_value = None
2171 model = juju.model.Model()
2172 result = self.libjuju._get_application_count(model, "app")
2173 self.assertEqual(result, None)
2174
2175
2176 @asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
2177 class GetMachineInfoTest(LibjujuTestCase):
2178 def setUp(self):
2179 super(GetMachineInfoTest, self).setUp()
2180
2181 def test_successful(
2182 self,
2183 mock_machines,
2184 ):
2185 machine_id = "existing_machine"
2186 model = juju.model.Model()
2187 mock_machines.return_value = {"existing_machine": FakeManualMachine()}
2188 machine, series = self.libjuju._get_machine_info(
2189 machine_id=machine_id,
2190 model=model,
2191 )
2192 self.assertIsNotNone(machine, series)
2193
2194 def test_exception(
2195 self,
2196 mock_machines,
2197 ):
2198 machine_id = "not_existing_machine"
2199 machine = series = None
2200 model = juju.model.Model()
2201 mock_machines.return_value = {"existing_machine": FakeManualMachine()}
2202 with self.assertRaises(JujuMachineNotFound):
2203 machine, series = self.libjuju._get_machine_info(
2204 machine_id=machine_id,
2205 model=model,
2206 )
2207 self.assertIsNone(machine, series)
2208
2209
2210 class GetUnitTest(LibjujuTestCase):
2211 def setUp(self):
2212 super(GetUnitTest, self).setUp()
2213
2214 def test_successful(self):
2215 result = self.libjuju._get_unit(FakeApplication(), "existing_machine_id")
2216 self.assertIsInstance(result, FakeUnit)
2217
2218 def test_return_none(self):
2219 result = self.libjuju._get_unit(FakeApplication(), "not_existing_machine_id")
2220 self.assertIsNone(result)
2221
2222
2223 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2224 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2225 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2226 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2227 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2228 class CheckApplicationExists(LibjujuTestCase):
2229 def setUp(self):
2230 super(CheckApplicationExists, self).setUp()
2231
2232 def test_successful(
2233 self,
2234 mock_get_application,
2235 mock_disconnect_controller,
2236 mock_disconnect_model,
2237 mock_get_model,
2238 mock_get_controller,
2239 ):
2240 mock_get_model.return_value = juju.model.Model()
2241 mock_get_application.return_value = FakeApplication()
2242 result = self.loop.run_until_complete(
2243 self.libjuju.check_application_exists(
2244 "model",
2245 "app",
2246 )
2247 )
2248 self.assertEqual(result, True)
2249
2250 mock_get_application.assert_called_once()
2251 mock_get_controller.assert_called_once()
2252 mock_get_model.assert_called_once()
2253 mock_disconnect_controller.assert_called_once()
2254 mock_disconnect_model.assert_called_once()
2255
2256 def test_no_application(
2257 self,
2258 mock_get_application,
2259 mock_disconnect_controller,
2260 mock_disconnect_model,
2261 mock_get_model,
2262 mock_get_controller,
2263 ):
2264 mock_get_model.return_value = juju.model.Model()
2265 mock_get_application.return_value = None
2266 result = self.loop.run_until_complete(
2267 self.libjuju.check_application_exists(
2268 "model",
2269 "app",
2270 )
2271 )
2272 self.assertEqual(result, False)
2273
2274 mock_get_application.assert_called_once()
2275 mock_get_controller.assert_called_once()
2276 mock_get_model.assert_called_once()
2277 mock_disconnect_controller.assert_called_once()
2278 mock_disconnect_model.assert_called_once()
2279
2280
2281 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2282 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2283 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2284 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2285 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2286 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_machine_info")
2287 class AddUnitTest(LibjujuTestCase):
2288 def setUp(self):
2289 super(AddUnitTest, self).setUp()
2290
2291 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
2292 @asynctest.mock.patch("asyncio.sleep")
2293 def test_successful(
2294 self,
2295 mock_sleep,
2296 mock_wait_for,
2297 mock_get_machine_info,
2298 mock_get_application,
2299 mock_disconnect_controller,
2300 mock_disconnect_model,
2301 mock_get_model,
2302 mock_get_controller,
2303 ):
2304 mock_get_model.return_value = juju.model.Model()
2305 mock_get_application.return_value = FakeApplication()
2306 mock_get_machine_info.return_value = FakeMachine(), "series"
2307 self.loop.run_until_complete(
2308 self.libjuju.add_unit(
2309 "existing_app",
2310 "model",
2311 "machine",
2312 )
2313 )
2314
2315 mock_wait_for.assert_called_once()
2316 mock_get_application.assert_called_once()
2317 mock_get_controller.assert_called_once()
2318 mock_get_model.assert_called_once()
2319 mock_disconnect_controller.assert_called_once()
2320 mock_disconnect_model.assert_called_once()
2321
2322 def test_no_app(
2323 self,
2324 mock_get_machine_info,
2325 mock_get_application,
2326 mock_disconnect_controller,
2327 mock_disconnect_model,
2328 mock_get_model,
2329 mock_get_controller,
2330 ):
2331 mock_get_model.return_value = juju.model.Model()
2332 mock_get_application.return_value = None
2333 with self.assertRaises(JujuApplicationNotFound):
2334 self.loop.run_until_complete(
2335 self.libjuju.add_unit(
2336 "existing_app",
2337 "model",
2338 "machine",
2339 )
2340 )
2341
2342 mock_get_application.assert_called_once()
2343 mock_get_controller.assert_called_once()
2344 mock_get_model.assert_called_once()
2345 mock_disconnect_controller.assert_called_once()
2346 mock_disconnect_model.assert_called_once()
2347
2348 def test_no_machine(
2349 self,
2350 mock_get_machine_info,
2351 mock_get_application,
2352 mock_disconnect_controller,
2353 mock_disconnect_model,
2354 mock_get_model,
2355 mock_get_controller,
2356 ):
2357 mock_get_model.return_value = juju.model.Model()
2358 mock_get_application.return_value = FakeApplication()
2359 mock_get_machine_info.side_effect = JujuMachineNotFound()
2360 with self.assertRaises(JujuMachineNotFound):
2361 self.loop.run_until_complete(
2362 self.libjuju.add_unit(
2363 "existing_app",
2364 "model",
2365 "machine",
2366 )
2367 )
2368
2369 mock_get_application.assert_called_once()
2370 mock_get_controller.assert_called_once()
2371 mock_get_model.assert_called_once()
2372 mock_disconnect_controller.assert_called_once()
2373 mock_disconnect_model.assert_called_once()
2374
2375
2376 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2377 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2378 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2379 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2380 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2381 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_unit")
2382 class DestroyUnitTest(LibjujuTestCase):
2383 def setUp(self):
2384 super(DestroyUnitTest, self).setUp()
2385
2386 @asynctest.mock.patch("asyncio.sleep")
2387 def test_successful(
2388 self,
2389 mock_sleep,
2390 mock_get_unit,
2391 mock_get_application,
2392 mock_disconnect_controller,
2393 mock_disconnect_model,
2394 mock_get_model,
2395 mock_get_controller,
2396 ):
2397 mock_get_model.return_value = juju.model.Model()
2398 mock_get_application.return_value = FakeApplication()
2399
2400 self.loop.run_until_complete(
2401 self.libjuju.destroy_unit("app", "model", "machine", 0)
2402 )
2403
2404 mock_get_unit.assert_called()
2405 mock_get_application.assert_called_once()
2406 mock_get_controller.assert_called_once()
2407 mock_get_model.assert_called_once()
2408 mock_disconnect_controller.assert_called_once()
2409 mock_disconnect_model.assert_called_once()
2410
2411 def test_no_app(
2412 self,
2413 mock_get_unit,
2414 mock_get_application,
2415 mock_disconnect_controller,
2416 mock_disconnect_model,
2417 mock_get_model,
2418 mock_get_controller,
2419 ):
2420 mock_get_model.return_value = juju.model.Model()
2421 mock_get_application.return_value = None
2422
2423 with self.assertRaises(JujuApplicationNotFound):
2424 self.loop.run_until_complete(
2425 self.libjuju.destroy_unit("app", "model", "machine")
2426 )
2427
2428 mock_get_application.assert_called_once()
2429 mock_get_controller.assert_called_once()
2430 mock_get_model.assert_called_once()
2431 mock_disconnect_controller.assert_called_once()
2432 mock_disconnect_model.assert_called_once()
2433
2434 def test_no_unit(
2435 self,
2436 mock_get_unit,
2437 mock_get_application,
2438 mock_disconnect_controller,
2439 mock_disconnect_model,
2440 mock_get_model,
2441 mock_get_controller,
2442 ):
2443 mock_get_model.return_value = juju.model.Model()
2444 mock_get_application.return_value = FakeApplication()
2445 mock_get_unit.return_value = None
2446
2447 with self.assertRaises(JujuError):
2448 self.loop.run_until_complete(
2449 self.libjuju.destroy_unit("app", "model", "machine")
2450 )
2451
2452 mock_get_unit.assert_called_once()
2453 mock_get_application.assert_called_once()
2454 mock_get_controller.assert_called_once()
2455 mock_get_model.assert_called_once()
2456 mock_disconnect_controller.assert_called_once()
2457 mock_disconnect_model.assert_called_once()