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