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