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