Add get_metrics command to n2vc_juju_conn and libjuju
[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 juju
18 from juju.errors import JujuAPIError
19 import logging
20 from .utils import FakeN2VC, FakeMachine, FakeApplication
21 from n2vc.libjuju import Libjuju
22 from n2vc.exceptions import (
23 JujuControllerFailedConnecting,
24 JujuModelAlreadyExists,
25 JujuMachineNotFound,
26 JujuApplicationNotFound,
27 JujuActionNotFound,
28 JujuApplicationExists,
29 )
30
31
32 class LibjujuTestCase(asynctest.TestCase):
33 @asynctest.mock.patch("juju.controller.Controller.update_endpoints")
34 @asynctest.mock.patch("juju.client.connector.Connector.connect")
35 @asynctest.mock.patch("juju.controller.Controller.connection")
36 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_api_endpoints_db")
37 def setUp(
38 self,
39 mock__get_api_endpoints_db=None,
40 mock_connection=None,
41 mock_connect=None,
42 mock_update_endpoints=None,
43 ):
44 loop = asyncio.get_event_loop()
45 n2vc = FakeN2VC()
46 mock__get_api_endpoints_db.return_value = ["127.0.0.1:17070"]
47 endpoints = "127.0.0.1:17070"
48 username = "admin"
49 password = "secret"
50 cacert = """
51 -----BEGIN CERTIFICATE-----
52 SOMECERT
53 -----END CERTIFICATE-----"""
54 self.libjuju = Libjuju(
55 endpoints,
56 "192.168.0.155:17070",
57 username,
58 password,
59 cacert,
60 loop,
61 log=None,
62 db={"get_one": []},
63 n2vc=n2vc,
64 apt_mirror="192.168.0.100",
65 enable_os_upgrade=True,
66 )
67 logging.disable(logging.CRITICAL)
68 loop.run_until_complete(self.libjuju.disconnect())
69
70
71 @asynctest.mock.patch("juju.controller.Controller.connect")
72 @asynctest.mock.patch(
73 "juju.controller.Controller.api_endpoints",
74 new_callable=asynctest.CoroutineMock(return_value=["127.0.0.1:17070"]),
75 )
76 @asynctest.mock.patch("n2vc.libjuju.Libjuju._update_api_endpoints_db")
77 class GetControllerTest(LibjujuTestCase):
78 def setUp(self):
79 super(GetControllerTest, self).setUp()
80
81 def test_diff_endpoint(
82 self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
83 ):
84 self.libjuju.endpoints = []
85 controller = self.loop.run_until_complete(self.libjuju.get_controller())
86 mock__update_api_endpoints_db.assert_called_once_with(["127.0.0.1:17070"])
87 self.assertIsInstance(controller, juju.controller.Controller)
88
89 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
90 def test_exception(
91 self,
92 mock_disconnect_controller,
93 mock__update_api_endpoints_db,
94 mock_api_endpoints,
95 mock_connect,
96 ):
97 self.libjuju.endpoints = []
98 mock__update_api_endpoints_db.side_effect = Exception()
99 controller = None
100 with self.assertRaises(JujuControllerFailedConnecting):
101 controller = self.loop.run_until_complete(self.libjuju.get_controller())
102 self.assertIsNone(controller)
103 mock_disconnect_controller.assert_called_once()
104
105 def test_same_endpoint_get_controller(
106 self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
107 ):
108 self.libjuju.endpoints = ["127.0.0.1:17070"]
109 controller = self.loop.run_until_complete(self.libjuju.get_controller())
110 mock__update_api_endpoints_db.assert_not_called()
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 with self.assertRaises(JujuModelAlreadyExists):
151 self.loop.run_until_complete(
152 self.libjuju.add_model("existing_model", "cloud")
153 )
154
155 mock_disconnect_controller.assert_called()
156
157 # TODO Check two job executing at the same time and one returning without doing anything.
158
159 def test_non_existing_model(
160 self,
161 mock_disconnect_model,
162 mock_disconnect_controller,
163 mock_add_model,
164 mock_model_exists,
165 mock_get_controller,
166 ):
167 mock_model_exists.return_value = False
168 mock_get_controller.return_value = juju.controller.Controller()
169
170 self.loop.run_until_complete(
171 self.libjuju.add_model("nonexisting_model", "cloud")
172 )
173
174 mock_add_model.assert_called_once()
175 mock_disconnect_controller.assert_called()
176 mock_disconnect_model.assert_called()
177
178
179 @asynctest.mock.patch("juju.controller.Controller.get_model")
180 class GetModelTest(LibjujuTestCase):
181 def setUp(self):
182 super(GetModelTest, self).setUp()
183
184 def test_get_model(
185 self, mock_get_model,
186 ):
187 mock_get_model.return_value = juju.model.Model()
188 model = self.loop.run_until_complete(
189 self.libjuju.get_model(juju.controller.Controller(), "model")
190 )
191 self.assertIsInstance(model, juju.model.Model)
192
193
194 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
195 @asynctest.mock.patch("juju.controller.Controller.list_models")
196 class ModelExistsTest(LibjujuTestCase):
197 def setUp(self):
198 super(ModelExistsTest, self).setUp()
199
200 async def test_existing_model(
201 self, mock_list_models, mock_get_controller,
202 ):
203 mock_list_models.return_value = ["existing_model"]
204 self.assertTrue(
205 await self.libjuju.model_exists(
206 "existing_model", juju.controller.Controller()
207 )
208 )
209
210 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
211 async def test_no_controller(
212 self, mock_disconnect_controller, mock_list_models, mock_get_controller,
213 ):
214 mock_list_models.return_value = ["existing_model"]
215 mock_get_controller.return_value = juju.controller.Controller()
216 self.assertTrue(await self.libjuju.model_exists("existing_model"))
217 mock_disconnect_controller.assert_called_once()
218
219 async def test_non_existing_model(
220 self, mock_list_models, mock_get_controller,
221 ):
222 mock_list_models.return_value = ["existing_model"]
223 self.assertFalse(
224 await self.libjuju.model_exists(
225 "not_existing_model", juju.controller.Controller()
226 )
227 )
228
229
230 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
231 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
232 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
233 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
234 @asynctest.mock.patch("juju.model.Model.get_status")
235 class GetModelStatusTest(LibjujuTestCase):
236 def setUp(self):
237 super(GetModelStatusTest, self).setUp()
238
239 def test_success(
240 self,
241 mock_get_status,
242 mock_disconnect_controller,
243 mock_disconnect_model,
244 mock_get_model,
245 mock_get_controller,
246 ):
247 mock_get_model.return_value = juju.model.Model()
248 mock_get_status.return_value = {"status"}
249
250 status = self.loop.run_until_complete(self.libjuju.get_model_status("model"))
251
252 mock_get_status.assert_called_once()
253 mock_disconnect_controller.assert_called_once()
254 mock_disconnect_model.assert_called_once()
255
256 self.assertEqual(status, {"status"})
257
258 def test_exception(
259 self,
260 mock_get_status,
261 mock_disconnect_controller,
262 mock_disconnect_model,
263 mock_get_model,
264 mock_get_controller,
265 ):
266 mock_get_model.return_value = juju.model.Model()
267 mock_get_status.side_effect = Exception()
268 status = None
269 with self.assertRaises(Exception):
270 status = self.loop.run_until_complete(
271 self.libjuju.get_model_status("model")
272 )
273
274 mock_disconnect_controller.assert_called_once()
275 mock_disconnect_model.assert_called_once()
276
277 self.assertIsNone(status)
278
279
280 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
281 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
282 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
283 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
284 @asynctest.mock.patch("juju.model.Model.get_machines")
285 @asynctest.mock.patch("juju.model.Model.add_machine")
286 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
287 class CreateMachineTest(LibjujuTestCase):
288 def setUp(self):
289 super(CreateMachineTest, self).setUp()
290
291 def test_existing_machine(
292 self,
293 mock_wait_for,
294 mock_add_machine,
295 mock_get_machines,
296 mock_disconnect_controller,
297 mock_disconnect_model,
298 mock_get_model,
299 mock_get_controller,
300 ):
301 mock_get_model.return_value = juju.model.Model()
302 mock_get_machines.return_value = {"existing_machine": FakeMachine()}
303 machine, bool_res = self.loop.run_until_complete(
304 self.libjuju.create_machine("model", "existing_machine")
305 )
306
307 self.assertIsInstance(machine, FakeMachine)
308 self.assertFalse(bool_res)
309
310 mock_disconnect_controller.assert_called()
311 mock_disconnect_model.assert_called()
312
313 def test_non_existing_machine(
314 self,
315 mock_wait_for,
316 mock_add_machine,
317 mock_get_machines,
318 mock_disconnect_controller,
319 mock_disconnect_model,
320 mock_get_model,
321 mock_get_controller,
322 ):
323 machine = None
324 bool_res = None
325 mock_get_model.return_value = juju.model.Model()
326 with self.assertRaises(JujuMachineNotFound):
327 machine, bool_res = self.loop.run_until_complete(
328 self.libjuju.create_machine("model", "non_existing_machine")
329 )
330 self.assertIsNone(machine)
331 self.assertIsNone(bool_res)
332
333 mock_disconnect_controller.assert_called()
334 mock_disconnect_model.assert_called()
335
336 def test_no_machine(
337 self,
338 mock_wait_for,
339 mock_add_machine,
340 mock_get_machines,
341 mock_disconnect_controller,
342 mock_disconnect_model,
343 mock_get_model,
344 mock_get_controller,
345 ):
346 mock_get_model.return_value = juju.model.Model()
347 mock_add_machine.return_value = FakeMachine()
348
349 machine, bool_res = self.loop.run_until_complete(
350 self.libjuju.create_machine("model")
351 )
352
353 self.assertIsInstance(machine, FakeMachine)
354 self.assertTrue(bool_res)
355
356 mock_wait_for.assert_called_once()
357 mock_add_machine.assert_called_once()
358
359 mock_disconnect_controller.assert_called()
360 mock_disconnect_model.assert_called()
361
362
363 # TODO test provision machine
364
365
366 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
367 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
368 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
369 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
370 @asynctest.mock.patch(
371 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
372 )
373 @asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
374 @asynctest.mock.patch("juju.model.Model.deploy")
375 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
376 @asynctest.mock.patch("n2vc.libjuju.Libjuju.create_machine")
377 class DeployCharmTest(LibjujuTestCase):
378 def setUp(self):
379 super(DeployCharmTest, self).setUp()
380
381 def test_existing_app(
382 self,
383 mock_create_machine,
384 mock_wait_for,
385 mock_deploy,
386 mock_machines,
387 mock_applications,
388 mock_disconnect_controller,
389 mock_disconnect_model,
390 mock_get_model,
391 mock_get_controller,
392 ):
393 mock_get_model.return_value = juju.model.Model()
394 mock_applications.return_value = {"existing_app"}
395
396 application = None
397 with self.assertRaises(JujuApplicationExists):
398 application = self.loop.run_until_complete(
399 self.libjuju.deploy_charm("existing_app", "path", "model", "machine",)
400 )
401 self.assertIsNone(application)
402
403 mock_disconnect_controller.assert_called()
404 mock_disconnect_model.assert_called()
405
406 def test_non_existing_machine(
407 self,
408 mock_create_machine,
409 mock_wait_for,
410 mock_deploy,
411 mock_machines,
412 mock_applications,
413 mock_disconnect_controller,
414 mock_disconnect_model,
415 mock_get_model,
416 mock_get_controller,
417 ):
418 mock_get_model.return_value = juju.model.Model()
419 mock_machines.return_value = {"existing_machine": FakeMachine()}
420 application = None
421 with self.assertRaises(JujuMachineNotFound):
422 application = self.loop.run_until_complete(
423 self.libjuju.deploy_charm("app", "path", "model", "machine",)
424 )
425
426 self.assertIsNone(application)
427
428 mock_disconnect_controller.assert_called()
429 mock_disconnect_model.assert_called()
430
431 def test_2_units(
432 self,
433 mock_create_machine,
434 mock_wait_for,
435 mock_deploy,
436 mock_machines,
437 mock_applications,
438 mock_disconnect_controller,
439 mock_disconnect_model,
440 mock_get_model,
441 mock_get_controller,
442 ):
443 mock_get_model.return_value = juju.model.Model()
444 mock_machines.return_value = {"existing_machine": FakeMachine()}
445 mock_create_machine.return_value = (FakeMachine(), "other")
446 mock_deploy.return_value = FakeApplication()
447 application = self.loop.run_until_complete(
448 self.libjuju.deploy_charm(
449 "app", "path", "model", "existing_machine", num_units=2,
450 )
451 )
452
453 self.assertIsInstance(application, FakeApplication)
454
455 mock_deploy.assert_called_once()
456 mock_wait_for.assert_called_once()
457
458 mock_create_machine.assert_called_once()
459
460 mock_disconnect_controller.assert_called()
461 mock_disconnect_model.assert_called()
462
463 def test_1_unit(
464 self,
465 mock_create_machine,
466 mock_wait_for,
467 mock_deploy,
468 mock_machines,
469 mock_applications,
470 mock_disconnect_controller,
471 mock_disconnect_model,
472 mock_get_model,
473 mock_get_controller,
474 ):
475 mock_get_model.return_value = juju.model.Model()
476 mock_machines.return_value = {"existing_machine": FakeMachine()}
477 mock_deploy.return_value = FakeApplication()
478 application = self.loop.run_until_complete(
479 self.libjuju.deploy_charm("app", "path", "model", "existing_machine")
480 )
481
482 self.assertIsInstance(application, FakeApplication)
483
484 mock_deploy.assert_called_once()
485 mock_wait_for.assert_called_once()
486
487 mock_disconnect_controller.assert_called()
488 mock_disconnect_model.assert_called()
489
490
491 @asynctest.mock.patch(
492 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
493 )
494 class GetApplicationTest(LibjujuTestCase):
495 def setUp(self):
496 super(GetApplicationTest, self).setUp()
497
498 def test_existing_application(
499 self, mock_applications,
500 ):
501 mock_applications.return_value = {"existing_app": "exists"}
502 model = juju.model.Model()
503 result = self.libjuju._get_application(model, "existing_app")
504 self.assertEqual(result, "exists")
505
506 def test_non_existing_application(
507 self, mock_applications,
508 ):
509 mock_applications.return_value = {"existing_app": "exists"}
510 model = juju.model.Model()
511 result = self.libjuju._get_application(model, "nonexisting_app")
512 self.assertIsNone(result)
513
514
515 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
516 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
517 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
518 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
519 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
520 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
521 @asynctest.mock.patch("juju.model.Model.get_action_output")
522 @asynctest.mock.patch("juju.model.Model.get_action_status")
523 class ExecuteActionTest(LibjujuTestCase):
524 def setUp(self):
525 super(ExecuteActionTest, self).setUp()
526
527 def test_no_application(
528 self,
529 mock_get_action_status,
530 mock_get_action_output,
531 mock_wait_for,
532 mock__get_application,
533 mock_disconnect_controller,
534 mock_disconnect_model,
535 mock_get_model,
536 mock_get_controller,
537 ):
538 mock__get_application.return_value = None
539 mock_get_model.return_value = juju.model.Model()
540 output = None
541 status = None
542 with self.assertRaises(JujuApplicationNotFound):
543 output, status = self.loop.run_until_complete(
544 self.libjuju.execute_action("app", "model", "action",)
545 )
546 self.assertIsNone(output)
547 self.assertIsNone(status)
548
549 mock_disconnect_controller.assert_called()
550 mock_disconnect_model.assert_called()
551
552 def test_no_action(
553 self,
554 mock_get_action_status,
555 mock_get_action_output,
556 mock_wait_for,
557 mock__get_application,
558 mock_disconnect_controller,
559 mock_disconnect_model,
560 mock_get_model,
561 mock_get_controller,
562 ):
563
564 mock_get_model.return_value = juju.model.Model()
565 mock__get_application.return_value = FakeApplication()
566 output = None
567 status = None
568 with self.assertRaises(JujuActionNotFound):
569 output, status = self.loop.run_until_complete(
570 self.libjuju.execute_action("app", "model", "action",)
571 )
572 self.assertIsNone(output)
573 self.assertIsNone(status)
574
575 mock_disconnect_controller.assert_called()
576 mock_disconnect_model.assert_called()
577
578 # TODO no leader unit found exception
579
580 def test_succesful_exec(
581 self,
582 mock_get_action_status,
583 mock_get_action_output,
584 mock_wait_for,
585 mock__get_application,
586 mock_disconnect_controller,
587 mock_disconnect_model,
588 mock_get_model,
589 mock_get_controller,
590 ):
591 mock_get_model.return_value = juju.model.Model()
592 mock__get_application.return_value = FakeApplication()
593 mock_get_action_output.return_value = "output"
594 mock_get_action_status.return_value = {"id": "status"}
595 output, status = self.loop.run_until_complete(
596 self.libjuju.execute_action("app", "model", "existing_action")
597 )
598 self.assertEqual(output, "output")
599 self.assertEqual(status, "status")
600
601 mock_wait_for.assert_called_once()
602
603 mock_disconnect_controller.assert_called()
604 mock_disconnect_model.assert_called()
605
606
607 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
608 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
609 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
610 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
611 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
612 class GetActionTest(LibjujuTestCase):
613 def setUp(self):
614 super(GetActionTest, self).setUp()
615
616 def test_exception(
617 self,
618 mock_get_application,
619 mock_disconnect_controller,
620 mock_disconnect_model,
621 mock_get_model,
622 mock_get_controller,
623 ):
624 mock_get_application.side_effect = Exception()
625 actions = None
626 with self.assertRaises(Exception):
627 actions = self.loop.run_until_complete(
628 self.libjuju.get_actions("app", "model")
629 )
630
631 self.assertIsNone(actions)
632 mock_disconnect_controller.assert_called_once()
633 mock_disconnect_model.assert_called_once()
634
635 def test_success(
636 self,
637 mock_get_application,
638 mock_disconnect_controller,
639 mock_disconnect_model,
640 mock_get_model,
641 mock_get_controller,
642 ):
643 mock_get_application.return_value = FakeApplication()
644
645 actions = self.loop.run_until_complete(self.libjuju.get_actions("app", "model"))
646
647 self.assertEqual(actions, ["existing_action"])
648
649 mock_get_controller.assert_called_once()
650 mock_get_model.assert_called_once()
651 mock_disconnect_controller.assert_called_once()
652 mock_disconnect_model.assert_called_once()
653
654
655 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
656 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
657 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
658 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
659 @asynctest.mock.patch("juju.application.Application.get_metrics")
660 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
661 class GetMetricsTest(LibjujuTestCase):
662 def setUp(self):
663 super(GetMetricsTest, self).setUp()
664
665 def test_get_metrics_success(
666 self,
667 mock_get_application,
668 mock_get_metrics,
669 mock_disconnect_controller,
670 mock_disconnect_model,
671 mock_get_model,
672 mock_get_controller,
673 ):
674 mock_get_application.return_value = FakeApplication()
675 mock_get_model.return_value = juju.model.Model()
676
677 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
678
679 mock_disconnect_controller.assert_called_once()
680 mock_disconnect_model.assert_called_once()
681
682 def test_get_metrics_exception(
683 self,
684 mock_get_application,
685 mock_get_metrics,
686 mock_disconnect_controller,
687 mock_disconnect_model,
688 mock_get_model,
689 mock_get_controller,
690 ):
691 mock_get_model.return_value = juju.model.Model()
692 mock_get_metrics.side_effect = Exception()
693 with self.assertRaises(Exception):
694 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
695
696 mock_disconnect_controller.assert_called_once()
697 mock_disconnect_model.assert_called_once()
698
699 def test_missing_args_exception(
700 self,
701 mock_get_application,
702 mock_get_metrics,
703 mock_disconnect_controller,
704 mock_disconnect_model,
705 mock_get_model,
706 mock_get_controller,
707 ):
708 mock_get_model.return_value = juju.model.Model()
709
710 with self.assertRaises(Exception):
711 self.loop.run_until_complete(self.libjuju.get_metrics("", ""))
712
713 mock_get_controller.assert_not_called()
714 mock_get_model.assert_not_called()
715 mock_disconnect_controller.assert_not_called()
716 mock_disconnect_model.assert_not_called()
717
718
719 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
720 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
721 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
722 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
723 @asynctest.mock.patch("juju.model.Model.add_relation")
724 class AddRelationTest(LibjujuTestCase):
725 def setUp(self):
726 super(AddRelationTest, self).setUp()
727
728 @asynctest.mock.patch("logging.Logger.warning")
729 def test_not_found(
730 self,
731 mock_warning,
732 mock_add_relation,
733 mock_disconnect_controller,
734 mock_disconnect_model,
735 mock_get_model,
736 mock_get_controller,
737 ):
738 # TODO in libjuju.py should this fail only with a log message?
739 result = {"error": "not found", "response": "response", "request-id": 1}
740
741 mock_get_model.return_value = juju.model.Model()
742 mock_add_relation.side_effect = JujuAPIError(result)
743
744 self.loop.run_until_complete(
745 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
746 )
747
748 mock_warning.assert_called_with("Relation not found: not found")
749 mock_disconnect_controller.assert_called_once()
750 mock_disconnect_model.assert_called_once()
751
752 @asynctest.mock.patch("logging.Logger.warning")
753 def test_already_exists(
754 self,
755 mock_warning,
756 mock_add_relation,
757 mock_disconnect_controller,
758 mock_disconnect_model,
759 mock_get_model,
760 mock_get_controller,
761 ):
762 # TODO in libjuju.py should this fail silently?
763 result = {"error": "already exists", "response": "response", "request-id": 1}
764
765 mock_get_model.return_value = juju.model.Model()
766 mock_add_relation.side_effect = JujuAPIError(result)
767
768 self.loop.run_until_complete(
769 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
770 )
771
772 mock_warning.assert_called_with("Relation already exists: already exists")
773 mock_disconnect_controller.assert_called_once()
774 mock_disconnect_model.assert_called_once()
775
776 def test_exception(
777 self,
778 mock_add_relation,
779 mock_disconnect_controller,
780 mock_disconnect_model,
781 mock_get_model,
782 mock_get_controller,
783 ):
784 mock_get_model.return_value = juju.model.Model()
785 result = {"error": "", "response": "response", "request-id": 1}
786 mock_add_relation.side_effect = JujuAPIError(result)
787
788 with self.assertRaises(JujuAPIError):
789 self.loop.run_until_complete(
790 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
791 )
792
793 mock_disconnect_controller.assert_called_once()
794 mock_disconnect_model.assert_called_once()
795
796 def test_success(
797 self,
798 mock_add_relation,
799 mock_disconnect_controller,
800 mock_disconnect_model,
801 mock_get_model,
802 mock_get_controller,
803 ):
804 mock_get_model.return_value = juju.model.Model()
805
806 self.loop.run_until_complete(
807 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
808 )
809
810 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
811 mock_disconnect_controller.assert_called_once()
812 mock_disconnect_model.assert_called_once()
813
814 def test_saas(
815 self,
816 mock_add_relation,
817 mock_disconnect_controller,
818 mock_disconnect_model,
819 mock_get_model,
820 mock_get_controller,
821 ):
822 mock_get_model.return_value = juju.model.Model()
823
824 self.loop.run_until_complete(
825 self.libjuju.add_relation("model", "app1:relation1", "saas_name",)
826 )
827
828 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
829 mock_disconnect_controller.assert_called_once()
830 mock_disconnect_model.assert_called_once()
831
832
833 # TODO destroy_model testcase
834
835
836 @asynctest.mock.patch("juju.model.Model.get_machines")
837 @asynctest.mock.patch("logging.Logger.debug")
838 class DestroyMachineTest(LibjujuTestCase):
839 def setUp(self):
840 super(DestroyMachineTest, self).setUp()
841
842 def test_success(
843 self, mock_debug, mock_get_machines,
844 ):
845 mock_get_machines.side_effect = [
846 {"machine": FakeMachine()},
847 {"machine": FakeMachine()},
848 {},
849 ]
850 self.loop.run_until_complete(
851 self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
852 )
853 calls = [
854 asynctest.call("Waiting for machine machine is destroyed"),
855 asynctest.call("Machine destroyed: machine"),
856 ]
857 mock_debug.assert_has_calls(calls)
858
859 def test_no_machine(
860 self, mock_debug, mock_get_machines,
861 ):
862 mock_get_machines.return_value = {}
863 self.loop.run_until_complete(
864 self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
865 )
866 mock_debug.assert_called_with("Machine not found: machine")
867
868
869 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
870 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
871 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
872 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
873 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
874 class ConfigureApplicationTest(LibjujuTestCase):
875 def setUp(self):
876 super(ConfigureApplicationTest, self).setUp()
877
878 def test_success(
879 self,
880 mock_get_application,
881 mock_disconnect_controller,
882 mock_disconnect_model,
883 mock_get_model,
884 mock_get_controller,
885 ):
886
887 mock_get_application.return_value = FakeApplication()
888
889 self.loop.run_until_complete(
890 self.libjuju.configure_application("model", "app", {"config"},)
891 )
892 mock_get_application.assert_called_once()
893 mock_disconnect_controller.assert_called_once()
894 mock_disconnect_model.assert_called_once()
895
896 def test_exception(
897 self,
898 mock_get_application,
899 mock_disconnect_controller,
900 mock_disconnect_model,
901 mock_get_model,
902 mock_get_controller,
903 ):
904
905 mock_get_application.side_effect = Exception()
906
907 with self.assertRaises(Exception):
908 self.loop.run_until_complete(
909 self.libjuju.configure_application("model", "app", {"config"},)
910 )
911 mock_disconnect_controller.assert_called_once()
912 mock_disconnect_model.assert_called_once()
913
914
915 # TODO _get_api_endpoints_db test case
916 # TODO _update_api_endpoints_db test case
917 # TODO healthcheck test case
918
919
920 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
921 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
922 @asynctest.mock.patch("juju.controller.Controller.list_models")
923 class ListModelsTest(LibjujuTestCase):
924 def setUp(self):
925 super(ListModelsTest, self).setUp()
926
927 def test_containing(
928 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
929 ):
930 mock_get_controller.return_value = juju.controller.Controller()
931 mock_list_models.return_value = ["existingmodel"]
932 models = self.loop.run_until_complete(self.libjuju.list_models("existing"))
933
934 mock_disconnect_controller.assert_called_once()
935 self.assertEquals(models, ["existingmodel"])
936
937 def test_not_containing(
938 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
939 ):
940 mock_get_controller.return_value = juju.controller.Controller()
941 mock_list_models.return_value = ["existingmodel", "model"]
942 models = self.loop.run_until_complete(self.libjuju.list_models("mdl"))
943
944 mock_disconnect_controller.assert_called_once()
945 self.assertEquals(models, [])
946
947 def test_no_contains_arg(
948 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
949 ):
950 mock_get_controller.return_value = juju.controller.Controller()
951 mock_list_models.return_value = ["existingmodel", "model"]
952 models = self.loop.run_until_complete(self.libjuju.list_models())
953
954 mock_disconnect_controller.assert_called_once()
955 self.assertEquals(models, ["existingmodel", "model"])
956
957
958 @asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
959 class ModelsExistTest(LibjujuTestCase):
960 def setUp(self):
961 super(ModelsExistTest, self).setUp()
962
963 def test_model_names_none(self, mock_list_models):
964 mock_list_models.return_value = []
965 with self.assertRaises(Exception):
966 self.loop.run_until_complete(self.libjuju.models_exist(None))
967
968 def test_model_names_empty(self, mock_list_models):
969 mock_list_models.return_value = []
970 with self.assertRaises(Exception):
971 (exist, non_existing_models) = self.loop.run_until_complete(
972 self.libjuju.models_exist([])
973 )
974
975 def test_model_names_not_existing(self, mock_list_models):
976 mock_list_models.return_value = ["prometheus", "grafana"]
977 (exist, non_existing_models) = self.loop.run_until_complete(
978 self.libjuju.models_exist(["prometheus2", "grafana"])
979 )
980 self.assertFalse(exist)
981 self.assertEqual(non_existing_models, ["prometheus2"])
982
983 def test_model_names_exist(self, mock_list_models):
984 mock_list_models.return_value = ["prometheus", "grafana"]
985 (exist, non_existing_models) = self.loop.run_until_complete(
986 self.libjuju.models_exist(["prometheus", "grafana"])
987 )
988 self.assertTrue(exist)
989 self.assertEqual(non_existing_models, [])
990
991
992 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
993 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
994 @asynctest.mock.patch("juju.controller.Controller.list_offers")
995 class ListOffers(LibjujuTestCase):
996 def setUp(self):
997 super(ListOffers, self).setUp()
998
999 def test_disconnect_controller(
1000 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1001 ):
1002 mock_get_controller.return_value = juju.controller.Controller()
1003 mock_list_offers.side_effect = Exception()
1004 with self.assertRaises(Exception):
1005 self.loop.run_until_complete(self.libjuju.list_offers("model"))
1006 mock_disconnect_controller.assert_called_once()
1007
1008 def test_empty_list(
1009 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1010 ):
1011 mock_get_controller.return_value = juju.controller.Controller()
1012 mock_list_offers.return_value = []
1013 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
1014 self.assertEqual(offers, [])
1015 mock_disconnect_controller.assert_called_once()
1016
1017 def test_non_empty_list(
1018 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1019 ):
1020 mock_get_controller.return_value = juju.controller.Controller()
1021 mock_list_offers.return_value = ["offer"]
1022 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
1023 self.assertEqual(offers, ["offer"])
1024 mock_disconnect_controller.assert_called_once()
1025
1026
1027 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1028 @asynctest.mock.patch("juju.controller.Controller.get_model")
1029 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1030 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1031 @asynctest.mock.patch("juju.model.Model.consume")
1032 class ConsumeTest(LibjujuTestCase):
1033 def setUp(self):
1034 super(ConsumeTest, self).setUp()
1035
1036 def test_consume(
1037 self,
1038 mock_consume,
1039 mock_disconnect_controller,
1040 mock_disconnect_model,
1041 mock_get_model,
1042 mock_get_controller,
1043 ):
1044 mock_get_controller.return_value = juju.controller.Controller()
1045 mock_get_model.return_value = juju.model.Model()
1046
1047 self.loop.run_until_complete(self.libjuju.consume("offer_url", "model_name"))
1048 mock_consume.assert_called_once()
1049 mock_disconnect_model.assert_called_once()
1050 mock_disconnect_controller.assert_called_once()
1051
1052 def test_parsing_error_exception(
1053 self,
1054 mock_consume,
1055 mock_disconnect_controller,
1056 mock_disconnect_model,
1057 mock_get_model,
1058 mock_get_controller,
1059 ):
1060 mock_get_controller.return_value = juju.controller.Controller()
1061 mock_get_model.return_value = juju.model.Model()
1062 mock_consume.side_effect = juju.offerendpoints.ParseError("")
1063
1064 with self.assertRaises(juju.offerendpoints.ParseError):
1065 self.loop.run_until_complete(
1066 self.libjuju.consume("offer_url", "model_name")
1067 )
1068 mock_consume.assert_called_once()
1069 mock_disconnect_model.assert_called_once()
1070 mock_disconnect_controller.assert_called_once()
1071
1072 def test_juju_error_exception(
1073 self,
1074 mock_consume,
1075 mock_disconnect_controller,
1076 mock_disconnect_model,
1077 mock_get_model,
1078 mock_get_controller,
1079 ):
1080 mock_get_controller.return_value = juju.controller.Controller()
1081 mock_get_model.return_value = juju.model.Model()
1082 mock_consume.side_effect = juju.errors.JujuError("")
1083
1084 with self.assertRaises(juju.errors.JujuError):
1085 self.loop.run_until_complete(
1086 self.libjuju.consume("offer_url", "model_name")
1087 )
1088 mock_consume.assert_called_once()
1089 mock_disconnect_model.assert_called_once()
1090 mock_disconnect_controller.assert_called_once()
1091
1092 def test_juju_api_error_exception(
1093 self,
1094 mock_consume,
1095 mock_disconnect_controller,
1096 mock_disconnect_model,
1097 mock_get_model,
1098 mock_get_controller,
1099 ):
1100 mock_get_controller.return_value = juju.controller.Controller()
1101 mock_get_model.return_value = juju.model.Model()
1102 mock_consume.side_effect = juju.errors.JujuAPIError(
1103 {"error": "", "response": "", "request-id": ""}
1104 )
1105
1106 with self.assertRaises(juju.errors.JujuAPIError):
1107 self.loop.run_until_complete(
1108 self.libjuju.consume("offer_url", "model_name")
1109 )
1110 mock_consume.assert_called_once()
1111 mock_disconnect_model.assert_called_once()
1112 mock_disconnect_controller.assert_called_once()
1113
1114
1115 @asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
1116 class AddK8sTest(LibjujuTestCase):
1117 def setUp(self):
1118 super(AddK8sTest, self).setUp()
1119 self.auth_data = {
1120 "server": "https://192.168.0.21:16443",
1121 "token": "1234",
1122 "cacert": "cacert",
1123 }
1124
1125 def test_add_k8s(self, mock_add_cloud):
1126 self.loop.run_until_complete(
1127 self.libjuju.add_k8s("cloud", self.auth_data, "storage_class")
1128 )
1129 mock_add_cloud.assert_called_once()
1130
1131 def test_add_k8s_exception(self, mock_add_cloud):
1132 mock_add_cloud.side_effect = Exception()
1133 with self.assertRaises(Exception):
1134 self.loop.run_until_complete(
1135 self.libjuju.add_k8s("cloud", self.auth_data, "storage_class")
1136 )
1137 mock_add_cloud.assert_called_once()
1138
1139 def test_add_k8s_missing_name(self, mock_add_cloud):
1140 with self.assertRaises(Exception):
1141 self.loop.run_until_complete(
1142 self.libjuju.add_k8s("", self.auth_data, "storage_class")
1143 )
1144 mock_add_cloud.assert_not_called()
1145
1146 def test_add_k8s_missing_storage_name(self, mock_add_cloud):
1147 with self.assertRaises(Exception):
1148 self.loop.run_until_complete(
1149 self.libjuju.add_k8s("cloud", self.auth_data, "")
1150 )
1151 mock_add_cloud.assert_not_called()
1152
1153 def test_add_k8s_missing_auth_data_keys(self, mock_add_cloud):
1154 with self.assertRaises(Exception):
1155 self.loop.run_until_complete(self.libjuju.add_k8s("cloud", {}, ""))
1156 mock_add_cloud.assert_not_called()
1157
1158
1159 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1160 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1161 @asynctest.mock.patch("juju.controller.Controller.add_cloud")
1162 @asynctest.mock.patch("juju.controller.Controller.add_credential")
1163 class AddCloudTest(LibjujuTestCase):
1164 def setUp(self):
1165 super(AddCloudTest, self).setUp()
1166 self.cloud = juju.client.client.Cloud()
1167 self.credential = juju.client.client.CloudCredential()
1168
1169 def test_add_cloud_with_credential(
1170 self,
1171 mock_add_credential,
1172 mock_add_cloud,
1173 mock_disconnect_controller,
1174 mock_get_controller,
1175 ):
1176 mock_get_controller.return_value = juju.controller.Controller()
1177
1178 cloud = self.loop.run_until_complete(
1179 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1180 )
1181 self.assertEqual(cloud, self.cloud)
1182 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1183 mock_add_credential.assert_called_once_with(
1184 "cloud", credential=self.credential, cloud="cloud"
1185 )
1186 mock_disconnect_controller.assert_called_once()
1187
1188 def test_add_cloud_no_credential(
1189 self,
1190 mock_add_credential,
1191 mock_add_cloud,
1192 mock_disconnect_controller,
1193 mock_get_controller,
1194 ):
1195 mock_get_controller.return_value = juju.controller.Controller()
1196
1197 cloud = self.loop.run_until_complete(
1198 self.libjuju.add_cloud("cloud", self.cloud)
1199 )
1200 self.assertEqual(cloud, self.cloud)
1201 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1202 mock_add_credential.assert_not_called()
1203 mock_disconnect_controller.assert_called_once()
1204
1205 def test_add_cloud_exception(
1206 self,
1207 mock_add_credential,
1208 mock_add_cloud,
1209 mock_disconnect_controller,
1210 mock_get_controller,
1211 ):
1212 mock_get_controller.return_value = juju.controller.Controller()
1213 mock_add_cloud.side_effect = Exception()
1214 with self.assertRaises(Exception):
1215 self.loop.run_until_complete(
1216 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1217 )
1218
1219 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1220 mock_add_credential.assert_not_called()
1221 mock_disconnect_controller.assert_called_once()
1222
1223 def test_add_credential_exception(
1224 self,
1225 mock_add_credential,
1226 mock_add_cloud,
1227 mock_disconnect_controller,
1228 mock_get_controller,
1229 ):
1230 mock_get_controller.return_value = juju.controller.Controller()
1231 mock_add_credential.side_effect = Exception()
1232 with self.assertRaises(Exception):
1233 self.loop.run_until_complete(
1234 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1235 )
1236
1237 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1238 mock_add_credential.assert_called_once_with(
1239 "cloud", credential=self.credential, cloud="cloud"
1240 )
1241 mock_disconnect_controller.assert_called_once()
1242
1243
1244 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1245 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1246 @asynctest.mock.patch("juju.controller.Controller.remove_cloud")
1247 class RemoveCloudTest(LibjujuTestCase):
1248 def setUp(self):
1249 super(RemoveCloudTest, self).setUp()
1250
1251 def test_remove_cloud(
1252 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1253 ):
1254 mock_get_controller.return_value = juju.controller.Controller()
1255
1256 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1257 mock_remove_cloud.assert_called_once_with("cloud")
1258 mock_disconnect_controller.assert_called_once()
1259
1260 def test_remove_cloud_exception(
1261 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1262 ):
1263 mock_get_controller.return_value = juju.controller.Controller()
1264 mock_remove_cloud.side_effect = Exception()
1265
1266 with self.assertRaises(Exception):
1267 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1268 mock_remove_cloud.assert_called_once_with("cloud")
1269 mock_disconnect_controller.assert_called_once()