Add add_k8s, add_cloud, and remove_cloud commands to libjuju.py and unit tests
[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.model.Model.add_relation")
660 class AddRelationTest(LibjujuTestCase):
661 def setUp(self):
662 super(AddRelationTest, self).setUp()
663
664 @asynctest.mock.patch("logging.Logger.warning")
665 def test_not_found(
666 self,
667 mock_warning,
668 mock_add_relation,
669 mock_disconnect_controller,
670 mock_disconnect_model,
671 mock_get_model,
672 mock_get_controller,
673 ):
674 # TODO in libjuju.py should this fail only with a log message?
675 result = {"error": "not found", "response": "response", "request-id": 1}
676
677 mock_get_model.return_value = juju.model.Model()
678 mock_add_relation.side_effect = JujuAPIError(result)
679
680 self.loop.run_until_complete(
681 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
682 )
683
684 mock_warning.assert_called_with("Relation not found: not found")
685 mock_disconnect_controller.assert_called_once()
686 mock_disconnect_model.assert_called_once()
687
688 @asynctest.mock.patch("logging.Logger.warning")
689 def test_already_exists(
690 self,
691 mock_warning,
692 mock_add_relation,
693 mock_disconnect_controller,
694 mock_disconnect_model,
695 mock_get_model,
696 mock_get_controller,
697 ):
698 # TODO in libjuju.py should this fail silently?
699 result = {"error": "already exists", "response": "response", "request-id": 1}
700
701 mock_get_model.return_value = juju.model.Model()
702 mock_add_relation.side_effect = JujuAPIError(result)
703
704 self.loop.run_until_complete(
705 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
706 )
707
708 mock_warning.assert_called_with("Relation already exists: already exists")
709 mock_disconnect_controller.assert_called_once()
710 mock_disconnect_model.assert_called_once()
711
712 def test_exception(
713 self,
714 mock_add_relation,
715 mock_disconnect_controller,
716 mock_disconnect_model,
717 mock_get_model,
718 mock_get_controller,
719 ):
720 mock_get_model.return_value = juju.model.Model()
721 result = {"error": "", "response": "response", "request-id": 1}
722 mock_add_relation.side_effect = JujuAPIError(result)
723
724 with self.assertRaises(JujuAPIError):
725 self.loop.run_until_complete(
726 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
727 )
728
729 mock_disconnect_controller.assert_called_once()
730 mock_disconnect_model.assert_called_once()
731
732 def test_success(
733 self,
734 mock_add_relation,
735 mock_disconnect_controller,
736 mock_disconnect_model,
737 mock_get_model,
738 mock_get_controller,
739 ):
740 mock_get_model.return_value = juju.model.Model()
741
742 self.loop.run_until_complete(
743 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
744 )
745
746 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
747 mock_disconnect_controller.assert_called_once()
748 mock_disconnect_model.assert_called_once()
749
750 def test_saas(
751 self,
752 mock_add_relation,
753 mock_disconnect_controller,
754 mock_disconnect_model,
755 mock_get_model,
756 mock_get_controller,
757 ):
758 mock_get_model.return_value = juju.model.Model()
759
760 self.loop.run_until_complete(
761 self.libjuju.add_relation("model", "app1:relation1", "saas_name",)
762 )
763
764 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
765 mock_disconnect_controller.assert_called_once()
766 mock_disconnect_model.assert_called_once()
767
768
769 # TODO destroy_model testcase
770
771
772 @asynctest.mock.patch("juju.model.Model.get_machines")
773 @asynctest.mock.patch("logging.Logger.debug")
774 class DestroyMachineTest(LibjujuTestCase):
775 def setUp(self):
776 super(DestroyMachineTest, self).setUp()
777
778 def test_success(
779 self, mock_debug, mock_get_machines,
780 ):
781 mock_get_machines.side_effect = [
782 {"machine": FakeMachine()},
783 {"machine": FakeMachine()},
784 {},
785 ]
786 self.loop.run_until_complete(
787 self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
788 )
789 calls = [
790 asynctest.call("Waiting for machine machine is destroyed"),
791 asynctest.call("Machine destroyed: machine"),
792 ]
793 mock_debug.assert_has_calls(calls)
794
795 def test_no_machine(
796 self, mock_debug, mock_get_machines,
797 ):
798 mock_get_machines.return_value = {}
799 self.loop.run_until_complete(
800 self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
801 )
802 mock_debug.assert_called_with("Machine not found: machine")
803
804
805 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
806 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
807 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
808 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
809 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
810 class ConfigureApplicationTest(LibjujuTestCase):
811 def setUp(self):
812 super(ConfigureApplicationTest, self).setUp()
813
814 def test_success(
815 self,
816 mock_get_application,
817 mock_disconnect_controller,
818 mock_disconnect_model,
819 mock_get_model,
820 mock_get_controller,
821 ):
822
823 mock_get_application.return_value = FakeApplication()
824
825 self.loop.run_until_complete(
826 self.libjuju.configure_application("model", "app", {"config"},)
827 )
828 mock_get_application.assert_called_once()
829 mock_disconnect_controller.assert_called_once()
830 mock_disconnect_model.assert_called_once()
831
832 def test_exception(
833 self,
834 mock_get_application,
835 mock_disconnect_controller,
836 mock_disconnect_model,
837 mock_get_model,
838 mock_get_controller,
839 ):
840
841 mock_get_application.side_effect = Exception()
842
843 with self.assertRaises(Exception):
844 self.loop.run_until_complete(
845 self.libjuju.configure_application("model", "app", {"config"},)
846 )
847 mock_disconnect_controller.assert_called_once()
848 mock_disconnect_model.assert_called_once()
849
850
851 # TODO _get_api_endpoints_db test case
852 # TODO _update_api_endpoints_db test case
853 # TODO healthcheck test case
854
855
856 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
857 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
858 @asynctest.mock.patch("juju.controller.Controller.list_models")
859 class ListModelsTest(LibjujuTestCase):
860 def setUp(self):
861 super(ListModelsTest, self).setUp()
862
863 def test_containing(
864 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
865 ):
866 mock_get_controller.return_value = juju.controller.Controller()
867 mock_list_models.return_value = ["existingmodel"]
868 models = self.loop.run_until_complete(self.libjuju.list_models("existing"))
869
870 mock_disconnect_controller.assert_called_once()
871 self.assertEquals(models, ["existingmodel"])
872
873 def test_not_containing(
874 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
875 ):
876 mock_get_controller.return_value = juju.controller.Controller()
877 mock_list_models.return_value = ["existingmodel", "model"]
878 models = self.loop.run_until_complete(self.libjuju.list_models("mdl"))
879
880 mock_disconnect_controller.assert_called_once()
881 self.assertEquals(models, [])
882
883 def test_no_contains_arg(
884 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
885 ):
886 mock_get_controller.return_value = juju.controller.Controller()
887 mock_list_models.return_value = ["existingmodel", "model"]
888 models = self.loop.run_until_complete(self.libjuju.list_models())
889
890 mock_disconnect_controller.assert_called_once()
891 self.assertEquals(models, ["existingmodel", "model"])
892
893
894 @asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
895 class ModelsExistTest(LibjujuTestCase):
896 def setUp(self):
897 super(ModelsExistTest, self).setUp()
898
899 def test_model_names_none(self, mock_list_models):
900 mock_list_models.return_value = []
901 with self.assertRaises(Exception):
902 self.loop.run_until_complete(self.libjuju.models_exist(None))
903
904 def test_model_names_empty(self, mock_list_models):
905 mock_list_models.return_value = []
906 with self.assertRaises(Exception):
907 (exist, non_existing_models) = self.loop.run_until_complete(
908 self.libjuju.models_exist([])
909 )
910
911 def test_model_names_not_existing(self, mock_list_models):
912 mock_list_models.return_value = ["prometheus", "grafana"]
913 (exist, non_existing_models) = self.loop.run_until_complete(
914 self.libjuju.models_exist(["prometheus2", "grafana"])
915 )
916 self.assertFalse(exist)
917 self.assertEqual(non_existing_models, ["prometheus2"])
918
919 def test_model_names_exist(self, mock_list_models):
920 mock_list_models.return_value = ["prometheus", "grafana"]
921 (exist, non_existing_models) = self.loop.run_until_complete(
922 self.libjuju.models_exist(["prometheus", "grafana"])
923 )
924 self.assertTrue(exist)
925 self.assertEqual(non_existing_models, [])
926
927
928 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
929 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
930 @asynctest.mock.patch("juju.controller.Controller.list_offers")
931 class ListOffers(LibjujuTestCase):
932 def setUp(self):
933 super(ListOffers, self).setUp()
934
935 def test_disconnect_controller(
936 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
937 ):
938 mock_get_controller.return_value = juju.controller.Controller()
939 mock_list_offers.side_effect = Exception()
940 with self.assertRaises(Exception):
941 self.loop.run_until_complete(self.libjuju.list_offers("model"))
942 mock_disconnect_controller.assert_called_once()
943
944 def test_empty_list(
945 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
946 ):
947 mock_get_controller.return_value = juju.controller.Controller()
948 mock_list_offers.return_value = []
949 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
950 self.assertEqual(offers, [])
951 mock_disconnect_controller.assert_called_once()
952
953 def test_non_empty_list(
954 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
955 ):
956 mock_get_controller.return_value = juju.controller.Controller()
957 mock_list_offers.return_value = ["offer"]
958 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
959 self.assertEqual(offers, ["offer"])
960 mock_disconnect_controller.assert_called_once()
961
962
963 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
964 @asynctest.mock.patch("juju.controller.Controller.get_model")
965 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
966 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
967 @asynctest.mock.patch("juju.model.Model.consume")
968 class ConsumeTest(LibjujuTestCase):
969 def setUp(self):
970 super(ConsumeTest, self).setUp()
971
972 def test_consume(
973 self,
974 mock_consume,
975 mock_disconnect_controller,
976 mock_disconnect_model,
977 mock_get_model,
978 mock_get_controller,
979 ):
980 mock_get_controller.return_value = juju.controller.Controller()
981 mock_get_model.return_value = juju.model.Model()
982
983 self.loop.run_until_complete(self.libjuju.consume("offer_url", "model_name"))
984 mock_consume.assert_called_once()
985 mock_disconnect_model.assert_called_once()
986 mock_disconnect_controller.assert_called_once()
987
988 def test_parsing_error_exception(
989 self,
990 mock_consume,
991 mock_disconnect_controller,
992 mock_disconnect_model,
993 mock_get_model,
994 mock_get_controller,
995 ):
996 mock_get_controller.return_value = juju.controller.Controller()
997 mock_get_model.return_value = juju.model.Model()
998 mock_consume.side_effect = juju.offerendpoints.ParseError("")
999
1000 with self.assertRaises(juju.offerendpoints.ParseError):
1001 self.loop.run_until_complete(
1002 self.libjuju.consume("offer_url", "model_name")
1003 )
1004 mock_consume.assert_called_once()
1005 mock_disconnect_model.assert_called_once()
1006 mock_disconnect_controller.assert_called_once()
1007
1008 def test_juju_error_exception(
1009 self,
1010 mock_consume,
1011 mock_disconnect_controller,
1012 mock_disconnect_model,
1013 mock_get_model,
1014 mock_get_controller,
1015 ):
1016 mock_get_controller.return_value = juju.controller.Controller()
1017 mock_get_model.return_value = juju.model.Model()
1018 mock_consume.side_effect = juju.errors.JujuError("")
1019
1020 with self.assertRaises(juju.errors.JujuError):
1021 self.loop.run_until_complete(
1022 self.libjuju.consume("offer_url", "model_name")
1023 )
1024 mock_consume.assert_called_once()
1025 mock_disconnect_model.assert_called_once()
1026 mock_disconnect_controller.assert_called_once()
1027
1028 def test_juju_api_error_exception(
1029 self,
1030 mock_consume,
1031 mock_disconnect_controller,
1032 mock_disconnect_model,
1033 mock_get_model,
1034 mock_get_controller,
1035 ):
1036 mock_get_controller.return_value = juju.controller.Controller()
1037 mock_get_model.return_value = juju.model.Model()
1038 mock_consume.side_effect = juju.errors.JujuAPIError(
1039 {"error": "", "response": "", "request-id": ""}
1040 )
1041
1042 with self.assertRaises(juju.errors.JujuAPIError):
1043 self.loop.run_until_complete(
1044 self.libjuju.consume("offer_url", "model_name")
1045 )
1046 mock_consume.assert_called_once()
1047 mock_disconnect_model.assert_called_once()
1048 mock_disconnect_controller.assert_called_once()
1049
1050
1051 @asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
1052 class AddK8sTest(LibjujuTestCase):
1053 def setUp(self):
1054 super(AddK8sTest, self).setUp()
1055 self.auth_data = {
1056 "server": "https://192.168.0.21:16443",
1057 "token": "1234",
1058 "cacert": "cacert",
1059 }
1060
1061 def test_add_k8s(self, mock_add_cloud):
1062 self.loop.run_until_complete(
1063 self.libjuju.add_k8s("cloud", self.auth_data, "storage_class")
1064 )
1065 mock_add_cloud.assert_called_once()
1066
1067 def test_add_k8s_exception(self, mock_add_cloud):
1068 mock_add_cloud.side_effect = Exception()
1069 with self.assertRaises(Exception):
1070 self.loop.run_until_complete(
1071 self.libjuju.add_k8s("cloud", self.auth_data, "storage_class")
1072 )
1073 mock_add_cloud.assert_called_once()
1074
1075 def test_add_k8s_missing_name(self, mock_add_cloud):
1076 with self.assertRaises(Exception):
1077 self.loop.run_until_complete(
1078 self.libjuju.add_k8s("", self.auth_data, "storage_class")
1079 )
1080 mock_add_cloud.assert_not_called()
1081
1082 def test_add_k8s_missing_storage_name(self, mock_add_cloud):
1083 with self.assertRaises(Exception):
1084 self.loop.run_until_complete(
1085 self.libjuju.add_k8s("cloud", self.auth_data, "")
1086 )
1087 mock_add_cloud.assert_not_called()
1088
1089 def test_add_k8s_missing_auth_data_keys(self, mock_add_cloud):
1090 with self.assertRaises(Exception):
1091 self.loop.run_until_complete(
1092 self.libjuju.add_k8s("cloud", {}, "")
1093 )
1094 mock_add_cloud.assert_not_called()
1095
1096
1097 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1098 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1099 @asynctest.mock.patch("juju.controller.Controller.add_cloud")
1100 @asynctest.mock.patch("juju.controller.Controller.add_credential")
1101 class AddCloudTest(LibjujuTestCase):
1102 def setUp(self):
1103 super(AddCloudTest, self).setUp()
1104 self.cloud = juju.client.client.Cloud()
1105 self.credential = juju.client.client.CloudCredential()
1106
1107 def test_add_cloud_with_credential(
1108 self,
1109 mock_add_credential,
1110 mock_add_cloud,
1111 mock_disconnect_controller,
1112 mock_get_controller,
1113 ):
1114 mock_get_controller.return_value = juju.controller.Controller()
1115
1116 cloud = self.loop.run_until_complete(
1117 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1118 )
1119 self.assertEqual(cloud, self.cloud)
1120 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1121 mock_add_credential.assert_called_once_with(
1122 "cloud", credential=self.credential, cloud="cloud"
1123 )
1124 mock_disconnect_controller.assert_called_once()
1125
1126 def test_add_cloud_no_credential(
1127 self,
1128 mock_add_credential,
1129 mock_add_cloud,
1130 mock_disconnect_controller,
1131 mock_get_controller,
1132 ):
1133 mock_get_controller.return_value = juju.controller.Controller()
1134
1135 cloud = self.loop.run_until_complete(
1136 self.libjuju.add_cloud("cloud", self.cloud)
1137 )
1138 self.assertEqual(cloud, self.cloud)
1139 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1140 mock_add_credential.assert_not_called()
1141 mock_disconnect_controller.assert_called_once()
1142
1143 def test_add_cloud_exception(
1144 self,
1145 mock_add_credential,
1146 mock_add_cloud,
1147 mock_disconnect_controller,
1148 mock_get_controller,
1149 ):
1150 mock_get_controller.return_value = juju.controller.Controller()
1151 mock_add_cloud.side_effect = Exception()
1152 with self.assertRaises(Exception):
1153 self.loop.run_until_complete(
1154 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1155 )
1156
1157 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1158 mock_add_credential.assert_not_called()
1159 mock_disconnect_controller.assert_called_once()
1160
1161 def test_add_credential_exception(
1162 self,
1163 mock_add_credential,
1164 mock_add_cloud,
1165 mock_disconnect_controller,
1166 mock_get_controller,
1167 ):
1168 mock_get_controller.return_value = juju.controller.Controller()
1169 mock_add_credential.side_effect = Exception()
1170 with self.assertRaises(Exception):
1171 self.loop.run_until_complete(
1172 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1173 )
1174
1175 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1176 mock_add_credential.assert_called_once_with(
1177 "cloud", credential=self.credential, cloud="cloud"
1178 )
1179 mock_disconnect_controller.assert_called_once()
1180
1181
1182 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1183 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1184 @asynctest.mock.patch("juju.controller.Controller.remove_cloud")
1185 class RemoveCloudTest(LibjujuTestCase):
1186 def setUp(self):
1187 super(RemoveCloudTest, self).setUp()
1188
1189 def test_remove_cloud(
1190 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1191 ):
1192 mock_get_controller.return_value = juju.controller.Controller()
1193
1194 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1195 mock_remove_cloud.assert_called_once_with("cloud")
1196 mock_disconnect_controller.assert_called_once()
1197
1198 def test_remove_cloud_exception(
1199 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1200 ):
1201 mock_get_controller.return_value = juju.controller.Controller()
1202 mock_remove_cloud.side_effect = Exception()
1203
1204 with self.assertRaises(Exception):
1205 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1206 mock_remove_cloud.assert_called_once_with("cloud")
1207 mock_disconnect_controller.assert_called_once()