454b87f7288a1d3d57e6af788429ba070bdc8a9f
[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 JujuModelAlreadyExists,
28 JujuMachineNotFound,
29 JujuApplicationNotFound,
30 JujuActionNotFound,
31 JujuApplicationExists,
32 JujuInvalidK8sConfiguration,
33 JujuLeaderUnitNotFound,
34 )
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 apt_mirror="192.168.0.100",
70 enable_os_upgrade=True,
71 )
72 logging.disable(logging.CRITICAL)
73 loop.run_until_complete(self.libjuju.disconnect())
74
75
76 @asynctest.mock.patch("n2vc.libjuju.Libjuju._create_health_check_task")
77 @asynctest.mock.patch("n2vc.libjuju.Libjuju._update_api_endpoints_db")
78 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_api_endpoints_db")
79 class LibjujuInitTestCase(asynctest.TestCase):
80 def setUp(self):
81 self.loop = asyncio.get_event_loop()
82 self.n2vc = FakeN2VC()
83 self.endpoint = "192.168.100.100:17070"
84 self.username = "admin"
85 self.password = "secret"
86 self.cacert = """
87 -----BEGIN CERTIFICATE-----
88 SOMECERT
89 -----END CERTIFICATE-----"""
90
91 def test_endpoint_not_in_db(
92 self,
93 mock__get_api_endpoints_db,
94 mock_update_endpoints,
95 mock_create_health_check_task,
96 ):
97 mock__get_api_endpoints_db.return_value = ["another_ip"]
98 Libjuju(
99 self.endpoint,
100 "192.168.0.155:17070",
101 self.username,
102 self.password,
103 self.cacert,
104 self.loop,
105 log=None,
106 db={"get_one": []},
107 n2vc=self.n2vc,
108 apt_mirror="192.168.0.100",
109 enable_os_upgrade=True,
110 )
111 mock_update_endpoints.assert_called_once_with([self.endpoint])
112 mock__get_api_endpoints_db.assert_called_once()
113
114 def test_endpoint_in_db(
115 self,
116 mock__get_api_endpoints_db,
117 mock_update_endpoints,
118 mock_create_health_check_task,
119 ):
120 mock__get_api_endpoints_db.return_value = [self.endpoint, "another_ip"]
121 Libjuju(
122 self.endpoint,
123 "192.168.0.155:17070",
124 self.username,
125 self.password,
126 self.cacert,
127 self.loop,
128 log=None,
129 db={"get_one": []},
130 n2vc=self.n2vc,
131 apt_mirror="192.168.0.100",
132 enable_os_upgrade=True,
133 )
134 mock_update_endpoints.assert_not_called()
135 mock__get_api_endpoints_db.assert_called_once()
136
137 def test_no_db_endpoints(
138 self,
139 mock__get_api_endpoints_db,
140 mock_update_endpoints,
141 mock_create_health_check_task,
142 ):
143 mock__get_api_endpoints_db.return_value = None
144 Libjuju(
145 self.endpoint,
146 "192.168.0.155:17070",
147 self.username,
148 self.password,
149 self.cacert,
150 self.loop,
151 log=None,
152 db={"get_one": []},
153 n2vc=self.n2vc,
154 apt_mirror="192.168.0.100",
155 enable_os_upgrade=True,
156 )
157 mock_update_endpoints.assert_called_once_with([self.endpoint])
158 mock__get_api_endpoints_db.assert_called_once()
159
160
161 @asynctest.mock.patch("juju.controller.Controller.connect")
162 @asynctest.mock.patch(
163 "juju.controller.Controller.api_endpoints",
164 new_callable=asynctest.CoroutineMock(return_value=["127.0.0.1:17070"]),
165 )
166 @asynctest.mock.patch("n2vc.libjuju.Libjuju._update_api_endpoints_db")
167 class GetControllerTest(LibjujuTestCase):
168 def setUp(self):
169 super(GetControllerTest, self).setUp()
170
171 def test_diff_endpoint(
172 self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
173 ):
174 self.libjuju.endpoints = []
175 controller = self.loop.run_until_complete(self.libjuju.get_controller())
176 mock__update_api_endpoints_db.assert_called_once_with(["127.0.0.1:17070"])
177 self.assertIsInstance(controller, juju.controller.Controller)
178
179 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
180 def test_exception(
181 self,
182 mock_disconnect_controller,
183 mock__update_api_endpoints_db,
184 mock_api_endpoints,
185 mock_connect,
186 ):
187 self.libjuju.endpoints = []
188 mock__update_api_endpoints_db.side_effect = Exception()
189 controller = None
190 with self.assertRaises(JujuControllerFailedConnecting):
191 controller = self.loop.run_until_complete(self.libjuju.get_controller())
192 self.assertIsNone(controller)
193 mock_disconnect_controller.assert_called_once()
194
195 def test_same_endpoint_get_controller(
196 self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
197 ):
198 self.libjuju.endpoints = ["127.0.0.1:17070"]
199 controller = self.loop.run_until_complete(self.libjuju.get_controller())
200 mock__update_api_endpoints_db.assert_not_called()
201 self.assertIsInstance(controller, juju.controller.Controller)
202
203
204 class DisconnectTest(LibjujuTestCase):
205 def setUp(self):
206 super(DisconnectTest, self).setUp()
207
208 @asynctest.mock.patch("juju.model.Model.disconnect")
209 def test_disconnect_model(self, mock_disconnect):
210 self.loop.run_until_complete(self.libjuju.disconnect_model(juju.model.Model()))
211 mock_disconnect.assert_called_once()
212
213 @asynctest.mock.patch("juju.controller.Controller.disconnect")
214 def test_disconnect_controller(self, mock_disconnect):
215 self.loop.run_until_complete(
216 self.libjuju.disconnect_controller(juju.controller.Controller())
217 )
218 mock_disconnect.assert_called_once()
219
220
221 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
222 @asynctest.mock.patch("n2vc.libjuju.Libjuju.model_exists")
223 @asynctest.mock.patch("juju.controller.Controller.add_model")
224 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
225 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
226 class AddModelTest(LibjujuTestCase):
227 def setUp(self):
228 super(AddModelTest, self).setUp()
229
230 def test_existing_model(
231 self,
232 mock_disconnect_model,
233 mock_disconnect_controller,
234 mock_add_model,
235 mock_model_exists,
236 mock_get_controller,
237 ):
238 mock_model_exists.return_value = True
239
240 with self.assertRaises(JujuModelAlreadyExists):
241 self.loop.run_until_complete(
242 self.libjuju.add_model("existing_model", "cloud")
243 )
244
245 mock_disconnect_controller.assert_called()
246
247 # TODO Check two job executing at the same time and one returning without doing anything.
248
249 def test_non_existing_model(
250 self,
251 mock_disconnect_model,
252 mock_disconnect_controller,
253 mock_add_model,
254 mock_model_exists,
255 mock_get_controller,
256 ):
257 mock_model_exists.return_value = False
258 mock_get_controller.return_value = juju.controller.Controller()
259
260 self.loop.run_until_complete(
261 self.libjuju.add_model("nonexisting_model", "cloud")
262 )
263
264 mock_add_model.assert_called_once()
265 mock_disconnect_controller.assert_called()
266 mock_disconnect_model.assert_called()
267
268
269 @asynctest.mock.patch("juju.controller.Controller.get_model")
270 class GetModelTest(LibjujuTestCase):
271 def setUp(self):
272 super(GetModelTest, self).setUp()
273
274 def test_get_model(
275 self, mock_get_model,
276 ):
277 mock_get_model.return_value = juju.model.Model()
278 model = self.loop.run_until_complete(
279 self.libjuju.get_model(juju.controller.Controller(), "model")
280 )
281 self.assertIsInstance(model, juju.model.Model)
282
283
284 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
285 @asynctest.mock.patch("juju.controller.Controller.list_models")
286 class ModelExistsTest(LibjujuTestCase):
287 def setUp(self):
288 super(ModelExistsTest, self).setUp()
289
290 async def test_existing_model(
291 self, mock_list_models, mock_get_controller,
292 ):
293 mock_list_models.return_value = ["existing_model"]
294 self.assertTrue(
295 await self.libjuju.model_exists(
296 "existing_model", juju.controller.Controller()
297 )
298 )
299
300 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
301 async def test_no_controller(
302 self, mock_disconnect_controller, mock_list_models, mock_get_controller,
303 ):
304 mock_list_models.return_value = ["existing_model"]
305 mock_get_controller.return_value = juju.controller.Controller()
306 self.assertTrue(await self.libjuju.model_exists("existing_model"))
307 mock_disconnect_controller.assert_called_once()
308
309 async def test_non_existing_model(
310 self, mock_list_models, 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("existing_app", "path", "model", "machine",)
555 )
556 self.assertIsNone(application)
557
558 mock_disconnect_controller.assert_called()
559 mock_disconnect_model.assert_called()
560
561 def test_non_existing_machine(
562 self,
563 mock_create_machine,
564 mock_wait_for,
565 mock_deploy,
566 mock_machines,
567 mock_applications,
568 mock_disconnect_controller,
569 mock_disconnect_model,
570 mock_get_model,
571 mock_get_controller,
572 ):
573 mock_get_model.return_value = juju.model.Model()
574 mock_machines.return_value = {"existing_machine": FakeMachine()}
575 application = None
576 with self.assertRaises(JujuMachineNotFound):
577 application = self.loop.run_until_complete(
578 self.libjuju.deploy_charm("app", "path", "model", "machine",)
579 )
580
581 self.assertIsNone(application)
582
583 mock_disconnect_controller.assert_called()
584 mock_disconnect_model.assert_called()
585
586 def test_2_units(
587 self,
588 mock_create_machine,
589 mock_wait_for,
590 mock_deploy,
591 mock_machines,
592 mock_applications,
593 mock_disconnect_controller,
594 mock_disconnect_model,
595 mock_get_model,
596 mock_get_controller,
597 ):
598 mock_get_model.return_value = juju.model.Model()
599 mock_machines.return_value = {"existing_machine": FakeMachine()}
600 mock_create_machine.return_value = (FakeMachine(), "other")
601 mock_deploy.return_value = FakeApplication()
602 application = self.loop.run_until_complete(
603 self.libjuju.deploy_charm(
604 "app", "path", "model", "existing_machine", num_units=2,
605 )
606 )
607
608 self.assertIsInstance(application, FakeApplication)
609
610 mock_deploy.assert_called_once()
611 mock_wait_for.assert_called_once()
612
613 mock_create_machine.assert_called_once()
614
615 mock_disconnect_controller.assert_called()
616 mock_disconnect_model.assert_called()
617
618 def test_1_unit(
619 self,
620 mock_create_machine,
621 mock_wait_for,
622 mock_deploy,
623 mock_machines,
624 mock_applications,
625 mock_disconnect_controller,
626 mock_disconnect_model,
627 mock_get_model,
628 mock_get_controller,
629 ):
630 mock_get_model.return_value = juju.model.Model()
631 mock_machines.return_value = {"existing_machine": FakeMachine()}
632 mock_deploy.return_value = FakeApplication()
633 application = self.loop.run_until_complete(
634 self.libjuju.deploy_charm("app", "path", "model", "existing_machine")
635 )
636
637 self.assertIsInstance(application, FakeApplication)
638
639 mock_deploy.assert_called_once()
640 mock_wait_for.assert_called_once()
641
642 mock_disconnect_controller.assert_called()
643 mock_disconnect_model.assert_called()
644
645
646 @asynctest.mock.patch(
647 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
648 )
649 class GetApplicationTest(LibjujuTestCase):
650 def setUp(self):
651 super(GetApplicationTest, self).setUp()
652
653 def test_existing_application(
654 self, mock_applications,
655 ):
656 mock_applications.return_value = {"existing_app": "exists"}
657 model = juju.model.Model()
658 result = self.libjuju._get_application(model, "existing_app")
659 self.assertEqual(result, "exists")
660
661 def test_non_existing_application(
662 self, mock_applications,
663 ):
664 mock_applications.return_value = {"existing_app": "exists"}
665 model = juju.model.Model()
666 result = self.libjuju._get_application(model, "nonexisting_app")
667 self.assertIsNone(result)
668
669
670 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
671 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
672 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
673 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
674 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
675 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
676 @asynctest.mock.patch("juju.model.Model.get_action_output")
677 @asynctest.mock.patch("juju.model.Model.get_action_status")
678 class ExecuteActionTest(LibjujuTestCase):
679 def setUp(self):
680 super(ExecuteActionTest, self).setUp()
681
682 def test_no_application(
683 self,
684 mock_get_action_status,
685 mock_get_action_output,
686 mock_wait_for,
687 mock__get_application,
688 mock_disconnect_controller,
689 mock_disconnect_model,
690 mock_get_model,
691 mock_get_controller,
692 ):
693 mock__get_application.return_value = None
694 mock_get_model.return_value = juju.model.Model()
695 output = None
696 status = None
697 with self.assertRaises(JujuApplicationNotFound):
698 output, status = self.loop.run_until_complete(
699 self.libjuju.execute_action("app", "model", "action",)
700 )
701 self.assertIsNone(output)
702 self.assertIsNone(status)
703
704 mock_disconnect_controller.assert_called()
705 mock_disconnect_model.assert_called()
706
707 def test_no_action(
708 self,
709 mock_get_action_status,
710 mock_get_action_output,
711 mock_wait_for,
712 mock__get_application,
713 mock_disconnect_controller,
714 mock_disconnect_model,
715 mock_get_model,
716 mock_get_controller,
717 ):
718
719 mock_get_model.return_value = juju.model.Model()
720 mock__get_application.return_value = FakeApplication()
721 output = None
722 status = None
723 with self.assertRaises(JujuActionNotFound):
724 output, status = self.loop.run_until_complete(
725 self.libjuju.execute_action("app", "model", "action",)
726 )
727 self.assertIsNone(output)
728 self.assertIsNone(status)
729
730 mock_disconnect_controller.assert_called()
731 mock_disconnect_model.assert_called()
732
733 @asynctest.mock.patch("asyncio.sleep")
734 @asynctest.mock.patch("n2vc.tests.unit.utils.FakeUnit.is_leader_from_status")
735 def test_no_leader(
736 self,
737 mock_is_leader_from_status,
738 mock_sleep,
739 mock_get_action_status,
740 mock_get_action_output,
741 mock_wait_for,
742 mock__get_application,
743 mock_disconnect_controller,
744 mock_disconnect_model,
745 mock_get_model,
746 mock_get_controller,
747 ):
748 mock_get_model.return_value = juju.model.Model()
749 mock__get_application.return_value = FakeApplication()
750 mock_is_leader_from_status.return_value = False
751 output = None
752 status = None
753 with self.assertRaises(JujuLeaderUnitNotFound):
754 output, status = self.loop.run_until_complete(
755 self.libjuju.execute_action("app", "model", "action",)
756 )
757 self.assertIsNone(output)
758 self.assertIsNone(status)
759
760 mock_disconnect_controller.assert_called()
761 mock_disconnect_model.assert_called()
762
763 def test_succesful_exec(
764 self,
765 mock_get_action_status,
766 mock_get_action_output,
767 mock_wait_for,
768 mock__get_application,
769 mock_disconnect_controller,
770 mock_disconnect_model,
771 mock_get_model,
772 mock_get_controller,
773 ):
774 mock_get_model.return_value = juju.model.Model()
775 mock__get_application.return_value = FakeApplication()
776 mock_get_action_output.return_value = "output"
777 mock_get_action_status.return_value = {"id": "status"}
778 output, status = self.loop.run_until_complete(
779 self.libjuju.execute_action("app", "model", "existing_action")
780 )
781 self.assertEqual(output, "output")
782 self.assertEqual(status, "status")
783
784 mock_wait_for.assert_called_once()
785
786 mock_disconnect_controller.assert_called()
787 mock_disconnect_model.assert_called()
788
789
790 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
791 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
792 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
793 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
794 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
795 class GetActionTest(LibjujuTestCase):
796 def setUp(self):
797 super(GetActionTest, self).setUp()
798
799 def test_exception(
800 self,
801 mock_get_application,
802 mock_disconnect_controller,
803 mock_disconnect_model,
804 mock_get_model,
805 mock_get_controller,
806 ):
807 mock_get_application.side_effect = Exception()
808 actions = None
809 with self.assertRaises(Exception):
810 actions = self.loop.run_until_complete(
811 self.libjuju.get_actions("app", "model")
812 )
813
814 self.assertIsNone(actions)
815 mock_disconnect_controller.assert_called_once()
816 mock_disconnect_model.assert_called_once()
817
818 def test_success(
819 self,
820 mock_get_application,
821 mock_disconnect_controller,
822 mock_disconnect_model,
823 mock_get_model,
824 mock_get_controller,
825 ):
826 mock_get_application.return_value = FakeApplication()
827
828 actions = self.loop.run_until_complete(self.libjuju.get_actions("app", "model"))
829
830 self.assertEqual(actions, ["existing_action"])
831
832 mock_get_controller.assert_called_once()
833 mock_get_model.assert_called_once()
834 mock_disconnect_controller.assert_called_once()
835 mock_disconnect_model.assert_called_once()
836
837
838 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
839 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
840 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
841 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
842 @asynctest.mock.patch("juju.application.Application.get_metrics")
843 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
844 class GetMetricsTest(LibjujuTestCase):
845 def setUp(self):
846 super(GetMetricsTest, self).setUp()
847
848 def test_get_metrics_success(
849 self,
850 mock_get_application,
851 mock_get_metrics,
852 mock_disconnect_controller,
853 mock_disconnect_model,
854 mock_get_model,
855 mock_get_controller,
856 ):
857 mock_get_application.return_value = FakeApplication()
858 mock_get_model.return_value = juju.model.Model()
859
860 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
861
862 mock_disconnect_controller.assert_called_once()
863 mock_disconnect_model.assert_called_once()
864
865 def test_get_metrics_exception(
866 self,
867 mock_get_application,
868 mock_get_metrics,
869 mock_disconnect_controller,
870 mock_disconnect_model,
871 mock_get_model,
872 mock_get_controller,
873 ):
874 mock_get_model.return_value = juju.model.Model()
875 mock_get_metrics.side_effect = Exception()
876 with self.assertRaises(Exception):
877 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
878
879 mock_disconnect_controller.assert_called_once()
880 mock_disconnect_model.assert_called_once()
881
882 def test_missing_args_exception(
883 self,
884 mock_get_application,
885 mock_get_metrics,
886 mock_disconnect_controller,
887 mock_disconnect_model,
888 mock_get_model,
889 mock_get_controller,
890 ):
891 mock_get_model.return_value = juju.model.Model()
892
893 with self.assertRaises(Exception):
894 self.loop.run_until_complete(self.libjuju.get_metrics("", ""))
895
896 mock_get_controller.assert_not_called()
897 mock_get_model.assert_not_called()
898 mock_disconnect_controller.assert_not_called()
899 mock_disconnect_model.assert_not_called()
900
901
902 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
903 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
904 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
905 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
906 @asynctest.mock.patch("juju.model.Model.add_relation")
907 class AddRelationTest(LibjujuTestCase):
908 def setUp(self):
909 super(AddRelationTest, self).setUp()
910
911 @asynctest.mock.patch("logging.Logger.warning")
912 def test_not_found(
913 self,
914 mock_warning,
915 mock_add_relation,
916 mock_disconnect_controller,
917 mock_disconnect_model,
918 mock_get_model,
919 mock_get_controller,
920 ):
921 # TODO in libjuju.py should this fail only with a log message?
922 result = {"error": "not found", "response": "response", "request-id": 1}
923
924 mock_get_model.return_value = juju.model.Model()
925 mock_add_relation.side_effect = JujuAPIError(result)
926
927 self.loop.run_until_complete(
928 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
929 )
930
931 mock_warning.assert_called_with("Relation not found: not found")
932 mock_disconnect_controller.assert_called_once()
933 mock_disconnect_model.assert_called_once()
934
935 @asynctest.mock.patch("logging.Logger.warning")
936 def test_already_exists(
937 self,
938 mock_warning,
939 mock_add_relation,
940 mock_disconnect_controller,
941 mock_disconnect_model,
942 mock_get_model,
943 mock_get_controller,
944 ):
945 # TODO in libjuju.py should this fail silently?
946 result = {"error": "already exists", "response": "response", "request-id": 1}
947
948 mock_get_model.return_value = juju.model.Model()
949 mock_add_relation.side_effect = JujuAPIError(result)
950
951 self.loop.run_until_complete(
952 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
953 )
954
955 mock_warning.assert_called_with("Relation already exists: already exists")
956 mock_disconnect_controller.assert_called_once()
957 mock_disconnect_model.assert_called_once()
958
959 def test_exception(
960 self,
961 mock_add_relation,
962 mock_disconnect_controller,
963 mock_disconnect_model,
964 mock_get_model,
965 mock_get_controller,
966 ):
967 mock_get_model.return_value = juju.model.Model()
968 result = {"error": "", "response": "response", "request-id": 1}
969 mock_add_relation.side_effect = JujuAPIError(result)
970
971 with self.assertRaises(JujuAPIError):
972 self.loop.run_until_complete(
973 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
974 )
975
976 mock_disconnect_controller.assert_called_once()
977 mock_disconnect_model.assert_called_once()
978
979 def test_success(
980 self,
981 mock_add_relation,
982 mock_disconnect_controller,
983 mock_disconnect_model,
984 mock_get_model,
985 mock_get_controller,
986 ):
987 mock_get_model.return_value = juju.model.Model()
988
989 self.loop.run_until_complete(
990 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
991 )
992
993 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
994 mock_disconnect_controller.assert_called_once()
995 mock_disconnect_model.assert_called_once()
996
997 def test_saas(
998 self,
999 mock_add_relation,
1000 mock_disconnect_controller,
1001 mock_disconnect_model,
1002 mock_get_model,
1003 mock_get_controller,
1004 ):
1005 mock_get_model.return_value = juju.model.Model()
1006
1007 self.loop.run_until_complete(
1008 self.libjuju.add_relation("model", "app1:relation1", "saas_name",)
1009 )
1010
1011 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
1012 mock_disconnect_controller.assert_called_once()
1013 mock_disconnect_model.assert_called_once()
1014
1015
1016 # TODO destroy_model testcase
1017
1018
1019 # @asynctest.mock.patch("juju.model.Model.get_machines")
1020 # @asynctest.mock.patch("logging.Logger.debug")
1021 # class DestroyMachineTest(LibjujuTestCase):
1022 # def setUp(self):
1023 # super(DestroyMachineTest, self).setUp()
1024
1025 # def test_success_manual_machine(
1026 # self, mock_debug, mock_get_machines,
1027 # ):
1028 # mock_get_machines.side_effect = [
1029 # {"machine": FakeManualMachine()},
1030 # {"machine": FakeManualMachine()},
1031 # {},
1032 # ]
1033 # self.loop.run_until_complete(
1034 # self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
1035 # )
1036 # calls = [
1037 # asynctest.call("Waiting for machine machine is destroyed"),
1038 # asynctest.call("Machine destroyed: machine"),
1039 # ]
1040 # mock_debug.assert_has_calls(calls)
1041
1042 # def test_no_machine(
1043 # self, mock_debug, mock_get_machines,
1044 # ):
1045 # mock_get_machines.return_value = {}
1046 # self.loop.run_until_complete(
1047 # self.libjuju.destroy_machine(juju.model.Model(), "machine", 2)
1048 # )
1049 # mock_debug.assert_called_with("Machine not found: machine")
1050
1051
1052 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1053 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1054 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1055 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1056 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1057 class ConfigureApplicationTest(LibjujuTestCase):
1058 def setUp(self):
1059 super(ConfigureApplicationTest, self).setUp()
1060
1061 def test_success(
1062 self,
1063 mock_get_application,
1064 mock_disconnect_controller,
1065 mock_disconnect_model,
1066 mock_get_model,
1067 mock_get_controller,
1068 ):
1069
1070 mock_get_application.return_value = FakeApplication()
1071
1072 self.loop.run_until_complete(
1073 self.libjuju.configure_application("model", "app", {"config"},)
1074 )
1075 mock_get_application.assert_called_once()
1076 mock_disconnect_controller.assert_called_once()
1077 mock_disconnect_model.assert_called_once()
1078
1079 def test_exception(
1080 self,
1081 mock_get_application,
1082 mock_disconnect_controller,
1083 mock_disconnect_model,
1084 mock_get_model,
1085 mock_get_controller,
1086 ):
1087
1088 mock_get_application.side_effect = Exception()
1089
1090 with self.assertRaises(Exception):
1091 self.loop.run_until_complete(
1092 self.libjuju.configure_application("model", "app", {"config"},)
1093 )
1094 mock_disconnect_controller.assert_called_once()
1095 mock_disconnect_model.assert_called_once()
1096
1097
1098 # TODO _get_api_endpoints_db test case
1099 # TODO _update_api_endpoints_db test case
1100 # TODO healthcheck test case
1101
1102
1103 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1104 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1105 @asynctest.mock.patch("juju.controller.Controller.list_models")
1106 class ListModelsTest(LibjujuTestCase):
1107 def setUp(self):
1108 super(ListModelsTest, self).setUp()
1109
1110 def test_containing(
1111 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
1112 ):
1113 mock_get_controller.return_value = juju.controller.Controller()
1114 mock_list_models.return_value = ["existingmodel"]
1115 models = self.loop.run_until_complete(self.libjuju.list_models("existing"))
1116
1117 mock_disconnect_controller.assert_called_once()
1118 self.assertEquals(models, ["existingmodel"])
1119
1120 def test_not_containing(
1121 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
1122 ):
1123 mock_get_controller.return_value = juju.controller.Controller()
1124 mock_list_models.return_value = ["existingmodel", "model"]
1125 models = self.loop.run_until_complete(self.libjuju.list_models("mdl"))
1126
1127 mock_disconnect_controller.assert_called_once()
1128 self.assertEquals(models, [])
1129
1130 def test_no_contains_arg(
1131 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
1132 ):
1133 mock_get_controller.return_value = juju.controller.Controller()
1134 mock_list_models.return_value = ["existingmodel", "model"]
1135 models = self.loop.run_until_complete(self.libjuju.list_models())
1136
1137 mock_disconnect_controller.assert_called_once()
1138 self.assertEquals(models, ["existingmodel", "model"])
1139
1140
1141 @asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
1142 class ModelsExistTest(LibjujuTestCase):
1143 def setUp(self):
1144 super(ModelsExistTest, self).setUp()
1145
1146 def test_model_names_none(self, mock_list_models):
1147 mock_list_models.return_value = []
1148 with self.assertRaises(Exception):
1149 self.loop.run_until_complete(self.libjuju.models_exist(None))
1150
1151 def test_model_names_empty(self, mock_list_models):
1152 mock_list_models.return_value = []
1153 with self.assertRaises(Exception):
1154 (exist, non_existing_models) = self.loop.run_until_complete(
1155 self.libjuju.models_exist([])
1156 )
1157
1158 def test_model_names_not_existing(self, mock_list_models):
1159 mock_list_models.return_value = ["prometheus", "grafana"]
1160 (exist, non_existing_models) = self.loop.run_until_complete(
1161 self.libjuju.models_exist(["prometheus2", "grafana"])
1162 )
1163 self.assertFalse(exist)
1164 self.assertEqual(non_existing_models, ["prometheus2"])
1165
1166 def test_model_names_exist(self, mock_list_models):
1167 mock_list_models.return_value = ["prometheus", "grafana"]
1168 (exist, non_existing_models) = self.loop.run_until_complete(
1169 self.libjuju.models_exist(["prometheus", "grafana"])
1170 )
1171 self.assertTrue(exist)
1172 self.assertEqual(non_existing_models, [])
1173
1174
1175 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1176 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1177 @asynctest.mock.patch("juju.controller.Controller.list_offers")
1178 class ListOffers(LibjujuTestCase):
1179 def setUp(self):
1180 super(ListOffers, self).setUp()
1181
1182 def test_disconnect_controller(
1183 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1184 ):
1185 mock_get_controller.return_value = juju.controller.Controller()
1186 mock_list_offers.side_effect = Exception()
1187 with self.assertRaises(Exception):
1188 self.loop.run_until_complete(self.libjuju.list_offers("model"))
1189 mock_disconnect_controller.assert_called_once()
1190
1191 def test_empty_list(
1192 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1193 ):
1194 mock_get_controller.return_value = juju.controller.Controller()
1195 mock_list_offers.return_value = []
1196 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
1197 self.assertEqual(offers, [])
1198 mock_disconnect_controller.assert_called_once()
1199
1200 def test_non_empty_list(
1201 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1202 ):
1203 mock_get_controller.return_value = juju.controller.Controller()
1204 mock_list_offers.return_value = ["offer"]
1205 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
1206 self.assertEqual(offers, ["offer"])
1207 mock_disconnect_controller.assert_called_once()
1208
1209
1210 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1211 @asynctest.mock.patch("juju.controller.Controller.get_model")
1212 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1213 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1214 @asynctest.mock.patch("juju.model.Model.consume")
1215 class ConsumeTest(LibjujuTestCase):
1216 def setUp(self):
1217 super(ConsumeTest, self).setUp()
1218
1219 def test_consume(
1220 self,
1221 mock_consume,
1222 mock_disconnect_controller,
1223 mock_disconnect_model,
1224 mock_get_model,
1225 mock_get_controller,
1226 ):
1227 mock_get_controller.return_value = juju.controller.Controller()
1228 mock_get_model.return_value = juju.model.Model()
1229
1230 self.loop.run_until_complete(self.libjuju.consume("offer_url", "model_name"))
1231 mock_consume.assert_called_once()
1232 mock_disconnect_model.assert_called_once()
1233 mock_disconnect_controller.assert_called_once()
1234
1235 def test_parsing_error_exception(
1236 self,
1237 mock_consume,
1238 mock_disconnect_controller,
1239 mock_disconnect_model,
1240 mock_get_model,
1241 mock_get_controller,
1242 ):
1243 mock_get_controller.return_value = juju.controller.Controller()
1244 mock_get_model.return_value = juju.model.Model()
1245 mock_consume.side_effect = juju.offerendpoints.ParseError("")
1246
1247 with self.assertRaises(juju.offerendpoints.ParseError):
1248 self.loop.run_until_complete(
1249 self.libjuju.consume("offer_url", "model_name")
1250 )
1251 mock_consume.assert_called_once()
1252 mock_disconnect_model.assert_called_once()
1253 mock_disconnect_controller.assert_called_once()
1254
1255 def test_juju_error_exception(
1256 self,
1257 mock_consume,
1258 mock_disconnect_controller,
1259 mock_disconnect_model,
1260 mock_get_model,
1261 mock_get_controller,
1262 ):
1263 mock_get_controller.return_value = juju.controller.Controller()
1264 mock_get_model.return_value = juju.model.Model()
1265 mock_consume.side_effect = juju.errors.JujuError("")
1266
1267 with self.assertRaises(juju.errors.JujuError):
1268 self.loop.run_until_complete(
1269 self.libjuju.consume("offer_url", "model_name")
1270 )
1271 mock_consume.assert_called_once()
1272 mock_disconnect_model.assert_called_once()
1273 mock_disconnect_controller.assert_called_once()
1274
1275 def test_juju_api_error_exception(
1276 self,
1277 mock_consume,
1278 mock_disconnect_controller,
1279 mock_disconnect_model,
1280 mock_get_model,
1281 mock_get_controller,
1282 ):
1283 mock_get_controller.return_value = juju.controller.Controller()
1284 mock_get_model.return_value = juju.model.Model()
1285 mock_consume.side_effect = juju.errors.JujuAPIError(
1286 {"error": "", "response": "", "request-id": ""}
1287 )
1288
1289 with self.assertRaises(juju.errors.JujuAPIError):
1290 self.loop.run_until_complete(
1291 self.libjuju.consume("offer_url", "model_name")
1292 )
1293 mock_consume.assert_called_once()
1294 mock_disconnect_model.assert_called_once()
1295 mock_disconnect_controller.assert_called_once()
1296
1297
1298 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_k8s_cloud_credential")
1299 @asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
1300 class AddK8sTest(LibjujuTestCase):
1301 def setUp(self):
1302 super(AddK8sTest, self).setUp()
1303 self.configuration = kubernetes.client.configuration.Configuration()
1304
1305 def test_add_k8s(self, mock_add_cloud, mock_get_k8s_cloud_credential):
1306 self.loop.run_until_complete(
1307 self.libjuju.add_k8s("cloud", self.configuration, "storage_class")
1308 )
1309 mock_add_cloud.assert_called_once()
1310 mock_get_k8s_cloud_credential.assert_called_once()
1311
1312 def test_add_k8s_exception(self, mock_add_cloud, mock_get_k8s_cloud_credential):
1313 mock_add_cloud.side_effect = Exception()
1314 with self.assertRaises(Exception):
1315 self.loop.run_until_complete(
1316 self.libjuju.add_k8s("cloud", self.configuration, "storage_class")
1317 )
1318 mock_add_cloud.assert_called_once()
1319 mock_get_k8s_cloud_credential.assert_called_once()
1320
1321 def test_add_k8s_missing_name(self, mock_add_cloud, mock_get_k8s_cloud_credential):
1322 with self.assertRaises(Exception):
1323 self.loop.run_until_complete(
1324 self.libjuju.add_k8s("", self.configuration, "storage_class")
1325 )
1326 mock_add_cloud.assert_not_called()
1327
1328 def test_add_k8s_missing_storage_name(
1329 self, mock_add_cloud, mock_get_k8s_cloud_credential
1330 ):
1331 with self.assertRaises(Exception):
1332 self.loop.run_until_complete(
1333 self.libjuju.add_k8s("cloud", self.configuration, "")
1334 )
1335 mock_add_cloud.assert_not_called()
1336
1337 def test_add_k8s_missing_configuration_keys(
1338 self, mock_add_cloud, mock_get_k8s_cloud_credential
1339 ):
1340 with self.assertRaises(Exception):
1341 self.loop.run_until_complete(self.libjuju.add_k8s("cloud", None, ""))
1342 mock_add_cloud.assert_not_called()
1343
1344
1345 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1346 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1347 @asynctest.mock.patch("juju.controller.Controller.add_cloud")
1348 @asynctest.mock.patch("juju.controller.Controller.add_credential")
1349 class AddCloudTest(LibjujuTestCase):
1350 def setUp(self):
1351 super(AddCloudTest, self).setUp()
1352 self.cloud = juju.client.client.Cloud()
1353 self.credential = juju.client.client.CloudCredential()
1354
1355 def test_add_cloud_with_credential(
1356 self,
1357 mock_add_credential,
1358 mock_add_cloud,
1359 mock_disconnect_controller,
1360 mock_get_controller,
1361 ):
1362 mock_get_controller.return_value = juju.controller.Controller()
1363
1364 cloud = self.loop.run_until_complete(
1365 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1366 )
1367 self.assertEqual(cloud, self.cloud)
1368 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1369 mock_add_credential.assert_called_once_with(
1370 "cloud", credential=self.credential, cloud="cloud"
1371 )
1372 mock_disconnect_controller.assert_called_once()
1373
1374 def test_add_cloud_no_credential(
1375 self,
1376 mock_add_credential,
1377 mock_add_cloud,
1378 mock_disconnect_controller,
1379 mock_get_controller,
1380 ):
1381 mock_get_controller.return_value = juju.controller.Controller()
1382
1383 cloud = self.loop.run_until_complete(
1384 self.libjuju.add_cloud("cloud", self.cloud)
1385 )
1386 self.assertEqual(cloud, self.cloud)
1387 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1388 mock_add_credential.assert_not_called()
1389 mock_disconnect_controller.assert_called_once()
1390
1391 def test_add_cloud_exception(
1392 self,
1393 mock_add_credential,
1394 mock_add_cloud,
1395 mock_disconnect_controller,
1396 mock_get_controller,
1397 ):
1398 mock_get_controller.return_value = juju.controller.Controller()
1399 mock_add_cloud.side_effect = Exception()
1400 with self.assertRaises(Exception):
1401 self.loop.run_until_complete(
1402 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1403 )
1404
1405 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1406 mock_add_credential.assert_not_called()
1407 mock_disconnect_controller.assert_called_once()
1408
1409 def test_add_credential_exception(
1410 self,
1411 mock_add_credential,
1412 mock_add_cloud,
1413 mock_disconnect_controller,
1414 mock_get_controller,
1415 ):
1416 mock_get_controller.return_value = juju.controller.Controller()
1417 mock_add_credential.side_effect = Exception()
1418 with self.assertRaises(Exception):
1419 self.loop.run_until_complete(
1420 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1421 )
1422
1423 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1424 mock_add_credential.assert_called_once_with(
1425 "cloud", credential=self.credential, cloud="cloud"
1426 )
1427 mock_disconnect_controller.assert_called_once()
1428
1429
1430 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1431 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1432 @asynctest.mock.patch("juju.controller.Controller.remove_cloud")
1433 class RemoveCloudTest(LibjujuTestCase):
1434 def setUp(self):
1435 super(RemoveCloudTest, self).setUp()
1436
1437 def test_remove_cloud(
1438 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1439 ):
1440 mock_get_controller.return_value = juju.controller.Controller()
1441
1442 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1443 mock_remove_cloud.assert_called_once_with("cloud")
1444 mock_disconnect_controller.assert_called_once()
1445
1446 def test_remove_cloud_exception(
1447 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1448 ):
1449 mock_get_controller.return_value = juju.controller.Controller()
1450 mock_remove_cloud.side_effect = Exception()
1451
1452 with self.assertRaises(Exception):
1453 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1454 mock_remove_cloud.assert_called_once_with("cloud")
1455 mock_disconnect_controller.assert_called_once()
1456
1457
1458 @asynctest.mock.patch("kubernetes.client.configuration.Configuration")
1459 class GetK8sCloudCredentials(LibjujuTestCase):
1460 def setUp(self):
1461 super(GetK8sCloudCredentials, self).setUp()
1462
1463 @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
1464 def test_not_supported(self, mock_exception, mock_configuration):
1465 mock_configuration.username = ""
1466 mock_configuration.password = ""
1467 mock_configuration.ssl_ca_cert = None
1468 mock_configuration.cert_file = None
1469 mock_configuration.key_file = None
1470 exception_raised = False
1471 try:
1472 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1473 except JujuInvalidK8sConfiguration as e:
1474 exception_raised = True
1475 self.assertEqual(
1476 e.message, "authentication method not supported",
1477 )
1478 self.assertTrue(exception_raised)
1479
1480 def test_user_pass(self, mock_configuration):
1481 mock_configuration.username = "admin"
1482 mock_configuration.password = "admin"
1483 mock_configuration.ssl_ca_cert = None
1484 mock_configuration.cert_file = None
1485 mock_configuration.key_file = None
1486 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1487 self.assertEqual(
1488 credential,
1489 juju.client._definitions.CloudCredential(
1490 attrs={"username": "admin", "password": "admin"}, auth_type="userpass"
1491 ),
1492 )
1493
1494 def test_user_no_pass(self, mock_configuration):
1495 mock_configuration.username = "admin"
1496 mock_configuration.password = ""
1497 mock_configuration.ssl_ca_cert = None
1498 mock_configuration.cert_file = None
1499 mock_configuration.key_file = None
1500 with mock.patch.object(self.libjuju.log, "debug") as mock_debug:
1501 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1502 self.assertEqual(
1503 credential,
1504 juju.client._definitions.CloudCredential(
1505 attrs={"username": "admin", "password": ""}, auth_type="userpass"
1506 ),
1507 )
1508 mock_debug.assert_called_once_with(
1509 "credential for user admin has empty password"
1510 )
1511
1512 def test_user_pass_with_cert(self, mock_configuration):
1513 mock_configuration.username = "admin"
1514 mock_configuration.password = "admin"
1515 ssl_ca_cert = tempfile.NamedTemporaryFile()
1516 with open(ssl_ca_cert.name, "w") as ssl_ca_cert_file:
1517 ssl_ca_cert_file.write("cacert")
1518 mock_configuration.ssl_ca_cert = ssl_ca_cert.name
1519 mock_configuration.cert_file = None
1520 mock_configuration.key_file = None
1521 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1522 self.assertEqual(
1523 credential,
1524 juju.client._definitions.CloudCredential(
1525 attrs={
1526 "username": "admin",
1527 "password": "admin",
1528 "ClientCertificateData": "cacert",
1529 },
1530 auth_type="userpasswithcert",
1531 ),
1532 )
1533
1534 def test_cert(self, mock_configuration):
1535 mock_configuration.username = ""
1536 mock_configuration.password = ""
1537 mock_configuration.api_key = {"authorization": "Bearer Token"}
1538 ssl_ca_cert = tempfile.NamedTemporaryFile()
1539 with open(ssl_ca_cert.name, "w") as ssl_ca_cert_file:
1540 ssl_ca_cert_file.write("cacert")
1541 mock_configuration.ssl_ca_cert = ssl_ca_cert.name
1542 mock_configuration.cert_file = None
1543 mock_configuration.key_file = None
1544 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1545 self.assertEqual(
1546 credential,
1547 juju.client._definitions.CloudCredential(
1548 attrs={"ClientCertificateData": "cacert", "Token": "Token"},
1549 auth_type="certificate",
1550 ),
1551 )
1552
1553 def test_oauth2(self, mock_configuration):
1554 mock_configuration.username = ""
1555 mock_configuration.password = ""
1556 mock_configuration.api_key = {"authorization": "Bearer Token"}
1557 key = tempfile.NamedTemporaryFile()
1558 with open(key.name, "w") as key_file:
1559 key_file.write("key")
1560 mock_configuration.ssl_ca_cert = None
1561 mock_configuration.cert_file = None
1562 mock_configuration.key_file = key.name
1563 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1564 self.assertEqual(
1565 credential,
1566 juju.client._definitions.CloudCredential(
1567 attrs={"ClientKeyData": "key", "Token": "Token"}, auth_type="oauth2",
1568 ),
1569 )
1570
1571 @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
1572 def test_oauth2_missing_token(self, mock_exception, mock_configuration):
1573 mock_configuration.username = ""
1574 mock_configuration.password = ""
1575 key = tempfile.NamedTemporaryFile()
1576 with open(key.name, "w") as key_file:
1577 key_file.write("key")
1578 mock_configuration.ssl_ca_cert = None
1579 mock_configuration.cert_file = None
1580 mock_configuration.key_file = key.name
1581 exception_raised = False
1582 try:
1583 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1584 except JujuInvalidK8sConfiguration as e:
1585 exception_raised = True
1586 self.assertEqual(
1587 e.message, "missing token for auth type oauth2",
1588 )
1589 self.assertTrue(exception_raised)
1590
1591 def test_unknown_api_key(self, mock_configuration):
1592 mock_configuration.username = ""
1593 mock_configuration.password = ""
1594 mock_configuration.api_key = {"authorization": "Bearer Token Wrong"}
1595 mock_configuration.ssl_ca_cert = None
1596 mock_configuration.cert_file = None
1597 mock_configuration.key_file = None
1598 exception_raised = False
1599 try:
1600 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1601 except JujuInvalidK8sConfiguration as e:
1602 exception_raised = True
1603 self.assertEqual(
1604 e.message, "unknown format of api_key",
1605 )
1606 self.assertTrue(exception_raised)
1607
1608 def test_exception_cannot_set_token_and_userpass(self, mock_configuration):
1609 mock_configuration.username = "admin"
1610 mock_configuration.password = "pass"
1611 mock_configuration.api_key = {"authorization": "No_bearer_token"}
1612 mock_configuration.ssl_ca_cert = None
1613 mock_configuration.cert_file = None
1614 mock_configuration.key_file = None
1615 exception_raised = False
1616 try:
1617 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1618 except JujuInvalidK8sConfiguration as e:
1619 exception_raised = True
1620 self.assertEqual(
1621 e.message, "Cannot set both token and user/pass",
1622 )
1623 self.assertTrue(exception_raised)