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