blob: 454b87f7288a1d3d57e6af788429ba070bdc8a9f [file] [log] [blame]
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001# 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
15import asyncio
16import asynctest
David Garcia475a7222020-09-21 16:19:15 +020017import tempfile
18from unittest import mock
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020019import juju
David Garcia475a7222020-09-21 16:19:15 +020020import kubernetes
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020021from juju.errors import JujuAPIError
22import logging
23from .utils import FakeN2VC, FakeMachine, FakeApplication
24from n2vc.libjuju import Libjuju
25from n2vc.exceptions import (
26 JujuControllerFailedConnecting,
27 JujuModelAlreadyExists,
28 JujuMachineNotFound,
29 JujuApplicationNotFound,
30 JujuActionNotFound,
31 JujuApplicationExists,
David Garcia475a7222020-09-21 16:19:15 +020032 JujuInvalidK8sConfiguration,
David Garcia59f520d2020-10-15 13:16:45 +020033 JujuLeaderUnitNotFound,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020034)
35
36
37class 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
David Garciaa4f57d62020-10-22 10:50:56 +020076@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")
79class 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
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200161@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")
167class 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()
David Garcia12b29242020-09-17 16:01:48 +0200189 controller = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200190 with self.assertRaises(JujuControllerFailedConnecting):
191 controller = self.loop.run_until_complete(self.libjuju.get_controller())
David Garcia12b29242020-09-17 16:01:48 +0200192 self.assertIsNone(controller)
193 mock_disconnect_controller.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200194
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
204class 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")
226class 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
David Garcia12b29242020-09-17 16:01:48 +0200245 mock_disconnect_controller.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200246
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")
270class 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")
286class 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")
325class 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
David Garcia12b29242020-09-17 16:01:48 +0200348 def test_exception(
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200349 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()
David Garcia12b29242020-09-17 16:01:48 +0200358 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200359 with self.assertRaises(Exception):
360 status = self.loop.run_until_complete(
361 self.libjuju.get_model_status("model")
362 )
363
David Garcia12b29242020-09-17 16:01:48 +0200364 mock_disconnect_controller.assert_called_once()
365 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200366
David Garcia12b29242020-09-17 16:01:48 +0200367 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200368
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")
377class 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 ):
David Garcia12b29242020-09-17 16:01:48 +0200413 machine = None
414 bool_res = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200415 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 )
David Garcia12b29242020-09-17 16:01:48 +0200420 self.assertIsNone(machine)
421 self.assertIsNone(bool_res)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200422
David Garcia12b29242020-09-17 16:01:48 +0200423 mock_disconnect_controller.assert_called()
424 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200425
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")
David Garcia667696e2020-09-22 14:52:32 +0200460@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for_model")
461@asynctest.mock.patch("juju.model.Model.deploy")
462class 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")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200525@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")
532class 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
David Garcia12b29242020-09-17 16:01:48 +0200551 application = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200552 with self.assertRaises(JujuApplicationExists):
553 application = self.loop.run_until_complete(
554 self.libjuju.deploy_charm("existing_app", "path", "model", "machine",)
555 )
David Garcia12b29242020-09-17 16:01:48 +0200556 self.assertIsNone(application)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200557
David Garcia12b29242020-09-17 16:01:48 +0200558 mock_disconnect_controller.assert_called()
559 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200560
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()}
David Garcia12b29242020-09-17 16:01:48 +0200575 application = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200576 with self.assertRaises(JujuMachineNotFound):
577 application = self.loop.run_until_complete(
578 self.libjuju.deploy_charm("app", "path", "model", "machine",)
579 )
580
David Garcia12b29242020-09-17 16:01:48 +0200581 self.assertIsNone(application)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200582
David Garcia12b29242020-09-17 16:01:48 +0200583 mock_disconnect_controller.assert_called()
584 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200585
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)
649class 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")
678class 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()
David Garcia12b29242020-09-17 16:01:48 +0200695 output = None
696 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200697 with self.assertRaises(JujuApplicationNotFound):
698 output, status = self.loop.run_until_complete(
699 self.libjuju.execute_action("app", "model", "action",)
700 )
David Garcia12b29242020-09-17 16:01:48 +0200701 self.assertIsNone(output)
702 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200703
David Garcia12b29242020-09-17 16:01:48 +0200704 mock_disconnect_controller.assert_called()
705 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200706
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()
David Garcia12b29242020-09-17 16:01:48 +0200721 output = None
722 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200723 with self.assertRaises(JujuActionNotFound):
724 output, status = self.loop.run_until_complete(
725 self.libjuju.execute_action("app", "model", "action",)
726 )
David Garcia12b29242020-09-17 16:01:48 +0200727 self.assertIsNone(output)
728 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200729
David Garcia12b29242020-09-17 16:01:48 +0200730 mock_disconnect_controller.assert_called()
731 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200732
David Garcia59f520d2020-10-15 13:16:45 +0200733 @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()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200762
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")
795class 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()
David Garcia12b29242020-09-17 16:01:48 +0200808 actions = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200809 with self.assertRaises(Exception):
810 actions = self.loop.run_until_complete(
811 self.libjuju.get_actions("app", "model")
812 )
813
David Garcia12b29242020-09-17 16:01:48 +0200814 self.assertIsNone(actions)
815 mock_disconnect_controller.assert_called_once()
816 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200817
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")
David Garcia85755d12020-09-21 19:51:23 +0200842@asynctest.mock.patch("juju.application.Application.get_metrics")
843@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
844class 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")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200906@asynctest.mock.patch("juju.model.Model.add_relation")
907class 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(
David Garcia8331f7c2020-08-25 16:10:07 +0200928 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200929 )
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(
David Garcia8331f7c2020-08-25 16:10:07 +0200952 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200953 )
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(
David Garcia8331f7c2020-08-25 16:10:07 +0200973 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200974 )
975
David Garcia12b29242020-09-17 16:01:48 +0200976 mock_disconnect_controller.assert_called_once()
977 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200978
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(
David Garcia8331f7c2020-08-25 16:10:07 +0200990 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200991 )
992
David Garcia12b29242020-09-17 16:01:48 +0200993 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
David Garcia8331f7c2020-08-25 16:10:07 +0200994 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
David Garcia12b29242020-09-17 16:01:48 +02001011 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001012 mock_disconnect_controller.assert_called_once()
1013 mock_disconnect_model.assert_called_once()
1014
1015
1016# TODO destroy_model testcase
1017
1018
David Garcia5ef42a12020-09-29 19:48:13 +02001019# @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()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001024
David Garcia5ef42a12020-09-29 19:48:13 +02001025# 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)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001041
David Garcia5ef42a12020-09-29 19:48:13 +02001042# 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")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001050
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")
1057class 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 )
David Garcia12b29242020-09-17 16:01:48 +02001094 mock_disconnect_controller.assert_called_once()
1095 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001096
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")
1106class 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"])
David Garcia42f328a2020-08-25 15:03:01 +02001139
1140
1141@asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
1142class 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, [])
David Garciabc538e42020-08-25 15:22:30 +02001173
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")
1178class 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()
David Garcia68b00722020-09-11 15:05:00 +02001208
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")
1215class 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
David Garcia12b29242020-09-17 16:01:48 +02001230 self.loop.run_until_complete(self.libjuju.consume("offer_url", "model_name"))
David Garcia68b00722020-09-11 15:05:00 +02001231 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()
David Garcia12b29242020-09-17 16:01:48 +02001285 mock_consume.side_effect = juju.errors.JujuAPIError(
1286 {"error": "", "response": "", "request-id": ""}
1287 )
David Garcia68b00722020-09-11 15:05:00 +02001288
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()
David Garcia12b29242020-09-17 16:01:48 +02001296
1297
David Garcia475a7222020-09-21 16:19:15 +02001298@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_k8s_cloud_credential")
David Garcia12b29242020-09-17 16:01:48 +02001299@asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
1300class AddK8sTest(LibjujuTestCase):
1301 def setUp(self):
1302 super(AddK8sTest, self).setUp()
David Garcia475a7222020-09-21 16:19:15 +02001303 self.configuration = kubernetes.client.configuration.Configuration()
David Garcia12b29242020-09-17 16:01:48 +02001304
David Garcia475a7222020-09-21 16:19:15 +02001305 def test_add_k8s(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02001306 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001307 self.libjuju.add_k8s("cloud", self.configuration, "storage_class")
David Garcia12b29242020-09-17 16:01:48 +02001308 )
1309 mock_add_cloud.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02001310 mock_get_k8s_cloud_credential.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02001311
David Garcia475a7222020-09-21 16:19:15 +02001312 def test_add_k8s_exception(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02001313 mock_add_cloud.side_effect = Exception()
1314 with self.assertRaises(Exception):
1315 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001316 self.libjuju.add_k8s("cloud", self.configuration, "storage_class")
David Garcia12b29242020-09-17 16:01:48 +02001317 )
1318 mock_add_cloud.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02001319 mock_get_k8s_cloud_credential.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02001320
David Garcia475a7222020-09-21 16:19:15 +02001321 def test_add_k8s_missing_name(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02001322 with self.assertRaises(Exception):
1323 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001324 self.libjuju.add_k8s("", self.configuration, "storage_class")
David Garcia12b29242020-09-17 16:01:48 +02001325 )
1326 mock_add_cloud.assert_not_called()
1327
David Garcia475a7222020-09-21 16:19:15 +02001328 def test_add_k8s_missing_storage_name(
1329 self, mock_add_cloud, mock_get_k8s_cloud_credential
1330 ):
David Garcia12b29242020-09-17 16:01:48 +02001331 with self.assertRaises(Exception):
1332 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001333 self.libjuju.add_k8s("cloud", self.configuration, "")
David Garcia12b29242020-09-17 16:01:48 +02001334 )
1335 mock_add_cloud.assert_not_called()
1336
David Garcia475a7222020-09-21 16:19:15 +02001337 def test_add_k8s_missing_configuration_keys(
1338 self, mock_add_cloud, mock_get_k8s_cloud_credential
1339 ):
David Garcia12b29242020-09-17 16:01:48 +02001340 with self.assertRaises(Exception):
David Garcia475a7222020-09-21 16:19:15 +02001341 self.loop.run_until_complete(self.libjuju.add_k8s("cloud", None, ""))
David Garcia12b29242020-09-17 16:01:48 +02001342 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")
1349class 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")
1433class 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()
David Garcia475a7222020-09-21 16:19:15 +02001456
1457
1458@asynctest.mock.patch("kubernetes.client.configuration.Configuration")
1459class 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)