blob: a384e447c1a0cb9b3c7e205e054593d8fee34cfb [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
76@asynctest.mock.patch("juju.controller.Controller.connect")
77@asynctest.mock.patch(
78 "juju.controller.Controller.api_endpoints",
79 new_callable=asynctest.CoroutineMock(return_value=["127.0.0.1:17070"]),
80)
81@asynctest.mock.patch("n2vc.libjuju.Libjuju._update_api_endpoints_db")
82class GetControllerTest(LibjujuTestCase):
83 def setUp(self):
84 super(GetControllerTest, self).setUp()
85
86 def test_diff_endpoint(
87 self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
88 ):
89 self.libjuju.endpoints = []
90 controller = self.loop.run_until_complete(self.libjuju.get_controller())
91 mock__update_api_endpoints_db.assert_called_once_with(["127.0.0.1:17070"])
92 self.assertIsInstance(controller, juju.controller.Controller)
93
94 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
95 def test_exception(
96 self,
97 mock_disconnect_controller,
98 mock__update_api_endpoints_db,
99 mock_api_endpoints,
100 mock_connect,
101 ):
102 self.libjuju.endpoints = []
103 mock__update_api_endpoints_db.side_effect = Exception()
David Garcia12b29242020-09-17 16:01:48 +0200104 controller = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200105 with self.assertRaises(JujuControllerFailedConnecting):
106 controller = self.loop.run_until_complete(self.libjuju.get_controller())
David Garcia12b29242020-09-17 16:01:48 +0200107 self.assertIsNone(controller)
108 mock_disconnect_controller.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200109
110 def test_same_endpoint_get_controller(
111 self, mock__update_api_endpoints_db, mock_api_endpoints, mock_connect
112 ):
113 self.libjuju.endpoints = ["127.0.0.1:17070"]
114 controller = self.loop.run_until_complete(self.libjuju.get_controller())
115 mock__update_api_endpoints_db.assert_not_called()
116 self.assertIsInstance(controller, juju.controller.Controller)
117
118
119class DisconnectTest(LibjujuTestCase):
120 def setUp(self):
121 super(DisconnectTest, self).setUp()
122
123 @asynctest.mock.patch("juju.model.Model.disconnect")
124 def test_disconnect_model(self, mock_disconnect):
125 self.loop.run_until_complete(self.libjuju.disconnect_model(juju.model.Model()))
126 mock_disconnect.assert_called_once()
127
128 @asynctest.mock.patch("juju.controller.Controller.disconnect")
129 def test_disconnect_controller(self, mock_disconnect):
130 self.loop.run_until_complete(
131 self.libjuju.disconnect_controller(juju.controller.Controller())
132 )
133 mock_disconnect.assert_called_once()
134
135
136@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
137@asynctest.mock.patch("n2vc.libjuju.Libjuju.model_exists")
138@asynctest.mock.patch("juju.controller.Controller.add_model")
139@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
140@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
141class AddModelTest(LibjujuTestCase):
142 def setUp(self):
143 super(AddModelTest, self).setUp()
144
145 def test_existing_model(
146 self,
147 mock_disconnect_model,
148 mock_disconnect_controller,
149 mock_add_model,
150 mock_model_exists,
151 mock_get_controller,
152 ):
153 mock_model_exists.return_value = True
154
155 with self.assertRaises(JujuModelAlreadyExists):
156 self.loop.run_until_complete(
157 self.libjuju.add_model("existing_model", "cloud")
158 )
159
David Garcia12b29242020-09-17 16:01:48 +0200160 mock_disconnect_controller.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200161
162 # TODO Check two job executing at the same time and one returning without doing anything.
163
164 def test_non_existing_model(
165 self,
166 mock_disconnect_model,
167 mock_disconnect_controller,
168 mock_add_model,
169 mock_model_exists,
170 mock_get_controller,
171 ):
172 mock_model_exists.return_value = False
173 mock_get_controller.return_value = juju.controller.Controller()
174
175 self.loop.run_until_complete(
176 self.libjuju.add_model("nonexisting_model", "cloud")
177 )
178
179 mock_add_model.assert_called_once()
180 mock_disconnect_controller.assert_called()
181 mock_disconnect_model.assert_called()
182
183
184@asynctest.mock.patch("juju.controller.Controller.get_model")
185class GetModelTest(LibjujuTestCase):
186 def setUp(self):
187 super(GetModelTest, self).setUp()
188
189 def test_get_model(
190 self, mock_get_model,
191 ):
192 mock_get_model.return_value = juju.model.Model()
193 model = self.loop.run_until_complete(
194 self.libjuju.get_model(juju.controller.Controller(), "model")
195 )
196 self.assertIsInstance(model, juju.model.Model)
197
198
199@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
200@asynctest.mock.patch("juju.controller.Controller.list_models")
201class ModelExistsTest(LibjujuTestCase):
202 def setUp(self):
203 super(ModelExistsTest, self).setUp()
204
205 async def test_existing_model(
206 self, mock_list_models, mock_get_controller,
207 ):
208 mock_list_models.return_value = ["existing_model"]
209 self.assertTrue(
210 await self.libjuju.model_exists(
211 "existing_model", juju.controller.Controller()
212 )
213 )
214
215 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
216 async def test_no_controller(
217 self, mock_disconnect_controller, mock_list_models, mock_get_controller,
218 ):
219 mock_list_models.return_value = ["existing_model"]
220 mock_get_controller.return_value = juju.controller.Controller()
221 self.assertTrue(await self.libjuju.model_exists("existing_model"))
222 mock_disconnect_controller.assert_called_once()
223
224 async def test_non_existing_model(
225 self, mock_list_models, mock_get_controller,
226 ):
227 mock_list_models.return_value = ["existing_model"]
228 self.assertFalse(
229 await self.libjuju.model_exists(
230 "not_existing_model", juju.controller.Controller()
231 )
232 )
233
234
235@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
236@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
237@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
238@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
239@asynctest.mock.patch("juju.model.Model.get_status")
240class GetModelStatusTest(LibjujuTestCase):
241 def setUp(self):
242 super(GetModelStatusTest, self).setUp()
243
244 def test_success(
245 self,
246 mock_get_status,
247 mock_disconnect_controller,
248 mock_disconnect_model,
249 mock_get_model,
250 mock_get_controller,
251 ):
252 mock_get_model.return_value = juju.model.Model()
253 mock_get_status.return_value = {"status"}
254
255 status = self.loop.run_until_complete(self.libjuju.get_model_status("model"))
256
257 mock_get_status.assert_called_once()
258 mock_disconnect_controller.assert_called_once()
259 mock_disconnect_model.assert_called_once()
260
261 self.assertEqual(status, {"status"})
262
David Garcia12b29242020-09-17 16:01:48 +0200263 def test_exception(
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200264 self,
265 mock_get_status,
266 mock_disconnect_controller,
267 mock_disconnect_model,
268 mock_get_model,
269 mock_get_controller,
270 ):
271 mock_get_model.return_value = juju.model.Model()
272 mock_get_status.side_effect = Exception()
David Garcia12b29242020-09-17 16:01:48 +0200273 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200274 with self.assertRaises(Exception):
275 status = self.loop.run_until_complete(
276 self.libjuju.get_model_status("model")
277 )
278
David Garcia12b29242020-09-17 16:01:48 +0200279 mock_disconnect_controller.assert_called_once()
280 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200281
David Garcia12b29242020-09-17 16:01:48 +0200282 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200283
284
285@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
286@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
287@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
288@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
289@asynctest.mock.patch("juju.model.Model.get_machines")
290@asynctest.mock.patch("juju.model.Model.add_machine")
291@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
292class CreateMachineTest(LibjujuTestCase):
293 def setUp(self):
294 super(CreateMachineTest, self).setUp()
295
296 def test_existing_machine(
297 self,
298 mock_wait_for,
299 mock_add_machine,
300 mock_get_machines,
301 mock_disconnect_controller,
302 mock_disconnect_model,
303 mock_get_model,
304 mock_get_controller,
305 ):
306 mock_get_model.return_value = juju.model.Model()
307 mock_get_machines.return_value = {"existing_machine": FakeMachine()}
308 machine, bool_res = self.loop.run_until_complete(
309 self.libjuju.create_machine("model", "existing_machine")
310 )
311
312 self.assertIsInstance(machine, FakeMachine)
313 self.assertFalse(bool_res)
314
315 mock_disconnect_controller.assert_called()
316 mock_disconnect_model.assert_called()
317
318 def test_non_existing_machine(
319 self,
320 mock_wait_for,
321 mock_add_machine,
322 mock_get_machines,
323 mock_disconnect_controller,
324 mock_disconnect_model,
325 mock_get_model,
326 mock_get_controller,
327 ):
David Garcia12b29242020-09-17 16:01:48 +0200328 machine = None
329 bool_res = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200330 mock_get_model.return_value = juju.model.Model()
331 with self.assertRaises(JujuMachineNotFound):
332 machine, bool_res = self.loop.run_until_complete(
333 self.libjuju.create_machine("model", "non_existing_machine")
334 )
David Garcia12b29242020-09-17 16:01:48 +0200335 self.assertIsNone(machine)
336 self.assertIsNone(bool_res)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200337
David Garcia12b29242020-09-17 16:01:48 +0200338 mock_disconnect_controller.assert_called()
339 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200340
341 def test_no_machine(
342 self,
343 mock_wait_for,
344 mock_add_machine,
345 mock_get_machines,
346 mock_disconnect_controller,
347 mock_disconnect_model,
348 mock_get_model,
349 mock_get_controller,
350 ):
351 mock_get_model.return_value = juju.model.Model()
352 mock_add_machine.return_value = FakeMachine()
353
354 machine, bool_res = self.loop.run_until_complete(
355 self.libjuju.create_machine("model")
356 )
357
358 self.assertIsInstance(machine, FakeMachine)
359 self.assertTrue(bool_res)
360
361 mock_wait_for.assert_called_once()
362 mock_add_machine.assert_called_once()
363
364 mock_disconnect_controller.assert_called()
365 mock_disconnect_model.assert_called()
366
367
368# TODO test provision machine
369
370
371@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
372@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
373@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
374@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
375@asynctest.mock.patch(
376 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
377)
378@asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
379@asynctest.mock.patch("juju.model.Model.deploy")
380@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
381@asynctest.mock.patch("n2vc.libjuju.Libjuju.create_machine")
382class DeployCharmTest(LibjujuTestCase):
383 def setUp(self):
384 super(DeployCharmTest, self).setUp()
385
386 def test_existing_app(
387 self,
388 mock_create_machine,
389 mock_wait_for,
390 mock_deploy,
391 mock_machines,
392 mock_applications,
393 mock_disconnect_controller,
394 mock_disconnect_model,
395 mock_get_model,
396 mock_get_controller,
397 ):
398 mock_get_model.return_value = juju.model.Model()
399 mock_applications.return_value = {"existing_app"}
400
David Garcia12b29242020-09-17 16:01:48 +0200401 application = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200402 with self.assertRaises(JujuApplicationExists):
403 application = self.loop.run_until_complete(
404 self.libjuju.deploy_charm("existing_app", "path", "model", "machine",)
405 )
David Garcia12b29242020-09-17 16:01:48 +0200406 self.assertIsNone(application)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200407
David Garcia12b29242020-09-17 16:01:48 +0200408 mock_disconnect_controller.assert_called()
409 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200410
411 def test_non_existing_machine(
412 self,
413 mock_create_machine,
414 mock_wait_for,
415 mock_deploy,
416 mock_machines,
417 mock_applications,
418 mock_disconnect_controller,
419 mock_disconnect_model,
420 mock_get_model,
421 mock_get_controller,
422 ):
423 mock_get_model.return_value = juju.model.Model()
424 mock_machines.return_value = {"existing_machine": FakeMachine()}
David Garcia12b29242020-09-17 16:01:48 +0200425 application = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200426 with self.assertRaises(JujuMachineNotFound):
427 application = self.loop.run_until_complete(
428 self.libjuju.deploy_charm("app", "path", "model", "machine",)
429 )
430
David Garcia12b29242020-09-17 16:01:48 +0200431 self.assertIsNone(application)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200432
David Garcia12b29242020-09-17 16:01:48 +0200433 mock_disconnect_controller.assert_called()
434 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200435
436 def test_2_units(
437 self,
438 mock_create_machine,
439 mock_wait_for,
440 mock_deploy,
441 mock_machines,
442 mock_applications,
443 mock_disconnect_controller,
444 mock_disconnect_model,
445 mock_get_model,
446 mock_get_controller,
447 ):
448 mock_get_model.return_value = juju.model.Model()
449 mock_machines.return_value = {"existing_machine": FakeMachine()}
450 mock_create_machine.return_value = (FakeMachine(), "other")
451 mock_deploy.return_value = FakeApplication()
452 application = self.loop.run_until_complete(
453 self.libjuju.deploy_charm(
454 "app", "path", "model", "existing_machine", num_units=2,
455 )
456 )
457
458 self.assertIsInstance(application, FakeApplication)
459
460 mock_deploy.assert_called_once()
461 mock_wait_for.assert_called_once()
462
463 mock_create_machine.assert_called_once()
464
465 mock_disconnect_controller.assert_called()
466 mock_disconnect_model.assert_called()
467
468 def test_1_unit(
469 self,
470 mock_create_machine,
471 mock_wait_for,
472 mock_deploy,
473 mock_machines,
474 mock_applications,
475 mock_disconnect_controller,
476 mock_disconnect_model,
477 mock_get_model,
478 mock_get_controller,
479 ):
480 mock_get_model.return_value = juju.model.Model()
481 mock_machines.return_value = {"existing_machine": FakeMachine()}
482 mock_deploy.return_value = FakeApplication()
483 application = self.loop.run_until_complete(
484 self.libjuju.deploy_charm("app", "path", "model", "existing_machine")
485 )
486
487 self.assertIsInstance(application, FakeApplication)
488
489 mock_deploy.assert_called_once()
490 mock_wait_for.assert_called_once()
491
492 mock_disconnect_controller.assert_called()
493 mock_disconnect_model.assert_called()
494
495
496@asynctest.mock.patch(
497 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
498)
499class GetApplicationTest(LibjujuTestCase):
500 def setUp(self):
501 super(GetApplicationTest, self).setUp()
502
503 def test_existing_application(
504 self, mock_applications,
505 ):
506 mock_applications.return_value = {"existing_app": "exists"}
507 model = juju.model.Model()
508 result = self.libjuju._get_application(model, "existing_app")
509 self.assertEqual(result, "exists")
510
511 def test_non_existing_application(
512 self, mock_applications,
513 ):
514 mock_applications.return_value = {"existing_app": "exists"}
515 model = juju.model.Model()
516 result = self.libjuju._get_application(model, "nonexisting_app")
517 self.assertIsNone(result)
518
519
520@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
521@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
522@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
523@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
524@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
525@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
526@asynctest.mock.patch("juju.model.Model.get_action_output")
527@asynctest.mock.patch("juju.model.Model.get_action_status")
528class ExecuteActionTest(LibjujuTestCase):
529 def setUp(self):
530 super(ExecuteActionTest, self).setUp()
531
532 def test_no_application(
533 self,
534 mock_get_action_status,
535 mock_get_action_output,
536 mock_wait_for,
537 mock__get_application,
538 mock_disconnect_controller,
539 mock_disconnect_model,
540 mock_get_model,
541 mock_get_controller,
542 ):
543 mock__get_application.return_value = None
544 mock_get_model.return_value = juju.model.Model()
David Garcia12b29242020-09-17 16:01:48 +0200545 output = None
546 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200547 with self.assertRaises(JujuApplicationNotFound):
548 output, status = self.loop.run_until_complete(
549 self.libjuju.execute_action("app", "model", "action",)
550 )
David Garcia12b29242020-09-17 16:01:48 +0200551 self.assertIsNone(output)
552 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200553
David Garcia12b29242020-09-17 16:01:48 +0200554 mock_disconnect_controller.assert_called()
555 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200556
557 def test_no_action(
558 self,
559 mock_get_action_status,
560 mock_get_action_output,
561 mock_wait_for,
562 mock__get_application,
563 mock_disconnect_controller,
564 mock_disconnect_model,
565 mock_get_model,
566 mock_get_controller,
567 ):
568
569 mock_get_model.return_value = juju.model.Model()
570 mock__get_application.return_value = FakeApplication()
David Garcia12b29242020-09-17 16:01:48 +0200571 output = None
572 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200573 with self.assertRaises(JujuActionNotFound):
574 output, status = self.loop.run_until_complete(
575 self.libjuju.execute_action("app", "model", "action",)
576 )
David Garcia12b29242020-09-17 16:01:48 +0200577 self.assertIsNone(output)
578 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200579
David Garcia12b29242020-09-17 16:01:48 +0200580 mock_disconnect_controller.assert_called()
581 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200582
David Garcia59f520d2020-10-15 13:16:45 +0200583 @asynctest.mock.patch("asyncio.sleep")
584 @asynctest.mock.patch("n2vc.tests.unit.utils.FakeUnit.is_leader_from_status")
585 def test_no_leader(
586 self,
587 mock_is_leader_from_status,
588 mock_sleep,
589 mock_get_action_status,
590 mock_get_action_output,
591 mock_wait_for,
592 mock__get_application,
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__get_application.return_value = FakeApplication()
600 mock_is_leader_from_status.return_value = False
601 output = None
602 status = None
603 with self.assertRaises(JujuLeaderUnitNotFound):
604 output, status = self.loop.run_until_complete(
605 self.libjuju.execute_action("app", "model", "action",)
606 )
607 self.assertIsNone(output)
608 self.assertIsNone(status)
609
610 mock_disconnect_controller.assert_called()
611 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200612
613 def test_succesful_exec(
614 self,
615 mock_get_action_status,
616 mock_get_action_output,
617 mock_wait_for,
618 mock__get_application,
619 mock_disconnect_controller,
620 mock_disconnect_model,
621 mock_get_model,
622 mock_get_controller,
623 ):
624 mock_get_model.return_value = juju.model.Model()
625 mock__get_application.return_value = FakeApplication()
626 mock_get_action_output.return_value = "output"
627 mock_get_action_status.return_value = {"id": "status"}
628 output, status = self.loop.run_until_complete(
629 self.libjuju.execute_action("app", "model", "existing_action")
630 )
631 self.assertEqual(output, "output")
632 self.assertEqual(status, "status")
633
634 mock_wait_for.assert_called_once()
635
636 mock_disconnect_controller.assert_called()
637 mock_disconnect_model.assert_called()
638
639
640@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
641@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
642@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
643@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
644@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
645class GetActionTest(LibjujuTestCase):
646 def setUp(self):
647 super(GetActionTest, self).setUp()
648
649 def test_exception(
650 self,
651 mock_get_application,
652 mock_disconnect_controller,
653 mock_disconnect_model,
654 mock_get_model,
655 mock_get_controller,
656 ):
657 mock_get_application.side_effect = Exception()
David Garcia12b29242020-09-17 16:01:48 +0200658 actions = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200659 with self.assertRaises(Exception):
660 actions = self.loop.run_until_complete(
661 self.libjuju.get_actions("app", "model")
662 )
663
David Garcia12b29242020-09-17 16:01:48 +0200664 self.assertIsNone(actions)
665 mock_disconnect_controller.assert_called_once()
666 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200667
668 def test_success(
669 self,
670 mock_get_application,
671 mock_disconnect_controller,
672 mock_disconnect_model,
673 mock_get_model,
674 mock_get_controller,
675 ):
676 mock_get_application.return_value = FakeApplication()
677
678 actions = self.loop.run_until_complete(self.libjuju.get_actions("app", "model"))
679
680 self.assertEqual(actions, ["existing_action"])
681
682 mock_get_controller.assert_called_once()
683 mock_get_model.assert_called_once()
684 mock_disconnect_controller.assert_called_once()
685 mock_disconnect_model.assert_called_once()
686
687
688@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
689@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
690@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
691@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
David Garcia85755d12020-09-21 19:51:23 +0200692@asynctest.mock.patch("juju.application.Application.get_metrics")
693@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
694class GetMetricsTest(LibjujuTestCase):
695 def setUp(self):
696 super(GetMetricsTest, self).setUp()
697
698 def test_get_metrics_success(
699 self,
700 mock_get_application,
701 mock_get_metrics,
702 mock_disconnect_controller,
703 mock_disconnect_model,
704 mock_get_model,
705 mock_get_controller,
706 ):
707 mock_get_application.return_value = FakeApplication()
708 mock_get_model.return_value = juju.model.Model()
709
710 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
711
712 mock_disconnect_controller.assert_called_once()
713 mock_disconnect_model.assert_called_once()
714
715 def test_get_metrics_exception(
716 self,
717 mock_get_application,
718 mock_get_metrics,
719 mock_disconnect_controller,
720 mock_disconnect_model,
721 mock_get_model,
722 mock_get_controller,
723 ):
724 mock_get_model.return_value = juju.model.Model()
725 mock_get_metrics.side_effect = Exception()
726 with self.assertRaises(Exception):
727 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
728
729 mock_disconnect_controller.assert_called_once()
730 mock_disconnect_model.assert_called_once()
731
732 def test_missing_args_exception(
733 self,
734 mock_get_application,
735 mock_get_metrics,
736 mock_disconnect_controller,
737 mock_disconnect_model,
738 mock_get_model,
739 mock_get_controller,
740 ):
741 mock_get_model.return_value = juju.model.Model()
742
743 with self.assertRaises(Exception):
744 self.loop.run_until_complete(self.libjuju.get_metrics("", ""))
745
746 mock_get_controller.assert_not_called()
747 mock_get_model.assert_not_called()
748 mock_disconnect_controller.assert_not_called()
749 mock_disconnect_model.assert_not_called()
750
751
752@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
753@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
754@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
755@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200756@asynctest.mock.patch("juju.model.Model.add_relation")
757class AddRelationTest(LibjujuTestCase):
758 def setUp(self):
759 super(AddRelationTest, self).setUp()
760
761 @asynctest.mock.patch("logging.Logger.warning")
762 def test_not_found(
763 self,
764 mock_warning,
765 mock_add_relation,
766 mock_disconnect_controller,
767 mock_disconnect_model,
768 mock_get_model,
769 mock_get_controller,
770 ):
771 # TODO in libjuju.py should this fail only with a log message?
772 result = {"error": "not found", "response": "response", "request-id": 1}
773
774 mock_get_model.return_value = juju.model.Model()
775 mock_add_relation.side_effect = JujuAPIError(result)
776
777 self.loop.run_until_complete(
David Garcia8331f7c2020-08-25 16:10:07 +0200778 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200779 )
780
781 mock_warning.assert_called_with("Relation not found: not found")
782 mock_disconnect_controller.assert_called_once()
783 mock_disconnect_model.assert_called_once()
784
785 @asynctest.mock.patch("logging.Logger.warning")
786 def test_already_exists(
787 self,
788 mock_warning,
789 mock_add_relation,
790 mock_disconnect_controller,
791 mock_disconnect_model,
792 mock_get_model,
793 mock_get_controller,
794 ):
795 # TODO in libjuju.py should this fail silently?
796 result = {"error": "already exists", "response": "response", "request-id": 1}
797
798 mock_get_model.return_value = juju.model.Model()
799 mock_add_relation.side_effect = JujuAPIError(result)
800
801 self.loop.run_until_complete(
David Garcia8331f7c2020-08-25 16:10:07 +0200802 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200803 )
804
805 mock_warning.assert_called_with("Relation already exists: already exists")
806 mock_disconnect_controller.assert_called_once()
807 mock_disconnect_model.assert_called_once()
808
809 def test_exception(
810 self,
811 mock_add_relation,
812 mock_disconnect_controller,
813 mock_disconnect_model,
814 mock_get_model,
815 mock_get_controller,
816 ):
817 mock_get_model.return_value = juju.model.Model()
818 result = {"error": "", "response": "response", "request-id": 1}
819 mock_add_relation.side_effect = JujuAPIError(result)
820
821 with self.assertRaises(JujuAPIError):
822 self.loop.run_until_complete(
David Garcia8331f7c2020-08-25 16:10:07 +0200823 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200824 )
825
David Garcia12b29242020-09-17 16:01:48 +0200826 mock_disconnect_controller.assert_called_once()
827 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200828
829 def test_success(
830 self,
831 mock_add_relation,
832 mock_disconnect_controller,
833 mock_disconnect_model,
834 mock_get_model,
835 mock_get_controller,
836 ):
837 mock_get_model.return_value = juju.model.Model()
838
839 self.loop.run_until_complete(
David Garcia8331f7c2020-08-25 16:10:07 +0200840 self.libjuju.add_relation("model", "app1:relation1", "app2:relation2",)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200841 )
842
David Garcia12b29242020-09-17 16:01:48 +0200843 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
David Garcia8331f7c2020-08-25 16:10:07 +0200844 mock_disconnect_controller.assert_called_once()
845 mock_disconnect_model.assert_called_once()
846
847 def test_saas(
848 self,
849 mock_add_relation,
850 mock_disconnect_controller,
851 mock_disconnect_model,
852 mock_get_model,
853 mock_get_controller,
854 ):
855 mock_get_model.return_value = juju.model.Model()
856
857 self.loop.run_until_complete(
858 self.libjuju.add_relation("model", "app1:relation1", "saas_name",)
859 )
860
David Garcia12b29242020-09-17 16:01:48 +0200861 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200862 mock_disconnect_controller.assert_called_once()
863 mock_disconnect_model.assert_called_once()
864
865
866# TODO destroy_model testcase
867
868
David Garcia5ef42a12020-09-29 19:48:13 +0200869# @asynctest.mock.patch("juju.model.Model.get_machines")
870# @asynctest.mock.patch("logging.Logger.debug")
871# class DestroyMachineTest(LibjujuTestCase):
872# def setUp(self):
873# super(DestroyMachineTest, self).setUp()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200874
David Garcia5ef42a12020-09-29 19:48:13 +0200875# def test_success_manual_machine(
876# self, mock_debug, mock_get_machines,
877# ):
878# mock_get_machines.side_effect = [
879# {"machine": FakeManualMachine()},
880# {"machine": FakeManualMachine()},
881# {},
882# ]
883# self.loop.run_until_complete(
884# self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
885# )
886# calls = [
887# asynctest.call("Waiting for machine machine is destroyed"),
888# asynctest.call("Machine destroyed: machine"),
889# ]
890# mock_debug.assert_has_calls(calls)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200891
David Garcia5ef42a12020-09-29 19:48:13 +0200892# def test_no_machine(
893# self, mock_debug, mock_get_machines,
894# ):
895# mock_get_machines.return_value = {}
896# self.loop.run_until_complete(
897# self.libjuju.destroy_machine(juju.model.Model(), "machine", 2)
898# )
899# mock_debug.assert_called_with("Machine not found: machine")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200900
901
902@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
903@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
904@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
905@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
906@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
907class ConfigureApplicationTest(LibjujuTestCase):
908 def setUp(self):
909 super(ConfigureApplicationTest, self).setUp()
910
911 def test_success(
912 self,
913 mock_get_application,
914 mock_disconnect_controller,
915 mock_disconnect_model,
916 mock_get_model,
917 mock_get_controller,
918 ):
919
920 mock_get_application.return_value = FakeApplication()
921
922 self.loop.run_until_complete(
923 self.libjuju.configure_application("model", "app", {"config"},)
924 )
925 mock_get_application.assert_called_once()
926 mock_disconnect_controller.assert_called_once()
927 mock_disconnect_model.assert_called_once()
928
929 def test_exception(
930 self,
931 mock_get_application,
932 mock_disconnect_controller,
933 mock_disconnect_model,
934 mock_get_model,
935 mock_get_controller,
936 ):
937
938 mock_get_application.side_effect = Exception()
939
940 with self.assertRaises(Exception):
941 self.loop.run_until_complete(
942 self.libjuju.configure_application("model", "app", {"config"},)
943 )
David Garcia12b29242020-09-17 16:01:48 +0200944 mock_disconnect_controller.assert_called_once()
945 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200946
947
948# TODO _get_api_endpoints_db test case
949# TODO _update_api_endpoints_db test case
950# TODO healthcheck test case
951
952
953@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
954@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
955@asynctest.mock.patch("juju.controller.Controller.list_models")
956class ListModelsTest(LibjujuTestCase):
957 def setUp(self):
958 super(ListModelsTest, self).setUp()
959
960 def test_containing(
961 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
962 ):
963 mock_get_controller.return_value = juju.controller.Controller()
964 mock_list_models.return_value = ["existingmodel"]
965 models = self.loop.run_until_complete(self.libjuju.list_models("existing"))
966
967 mock_disconnect_controller.assert_called_once()
968 self.assertEquals(models, ["existingmodel"])
969
970 def test_not_containing(
971 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
972 ):
973 mock_get_controller.return_value = juju.controller.Controller()
974 mock_list_models.return_value = ["existingmodel", "model"]
975 models = self.loop.run_until_complete(self.libjuju.list_models("mdl"))
976
977 mock_disconnect_controller.assert_called_once()
978 self.assertEquals(models, [])
979
980 def test_no_contains_arg(
981 self, mock_list_models, mock_disconnect_controller, mock_get_controller,
982 ):
983 mock_get_controller.return_value = juju.controller.Controller()
984 mock_list_models.return_value = ["existingmodel", "model"]
985 models = self.loop.run_until_complete(self.libjuju.list_models())
986
987 mock_disconnect_controller.assert_called_once()
988 self.assertEquals(models, ["existingmodel", "model"])
David Garcia42f328a2020-08-25 15:03:01 +0200989
990
991@asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
992class ModelsExistTest(LibjujuTestCase):
993 def setUp(self):
994 super(ModelsExistTest, self).setUp()
995
996 def test_model_names_none(self, mock_list_models):
997 mock_list_models.return_value = []
998 with self.assertRaises(Exception):
999 self.loop.run_until_complete(self.libjuju.models_exist(None))
1000
1001 def test_model_names_empty(self, mock_list_models):
1002 mock_list_models.return_value = []
1003 with self.assertRaises(Exception):
1004 (exist, non_existing_models) = self.loop.run_until_complete(
1005 self.libjuju.models_exist([])
1006 )
1007
1008 def test_model_names_not_existing(self, mock_list_models):
1009 mock_list_models.return_value = ["prometheus", "grafana"]
1010 (exist, non_existing_models) = self.loop.run_until_complete(
1011 self.libjuju.models_exist(["prometheus2", "grafana"])
1012 )
1013 self.assertFalse(exist)
1014 self.assertEqual(non_existing_models, ["prometheus2"])
1015
1016 def test_model_names_exist(self, mock_list_models):
1017 mock_list_models.return_value = ["prometheus", "grafana"]
1018 (exist, non_existing_models) = self.loop.run_until_complete(
1019 self.libjuju.models_exist(["prometheus", "grafana"])
1020 )
1021 self.assertTrue(exist)
1022 self.assertEqual(non_existing_models, [])
David Garciabc538e42020-08-25 15:22:30 +02001023
1024
1025@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1026@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1027@asynctest.mock.patch("juju.controller.Controller.list_offers")
1028class ListOffers(LibjujuTestCase):
1029 def setUp(self):
1030 super(ListOffers, self).setUp()
1031
1032 def test_disconnect_controller(
1033 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1034 ):
1035 mock_get_controller.return_value = juju.controller.Controller()
1036 mock_list_offers.side_effect = Exception()
1037 with self.assertRaises(Exception):
1038 self.loop.run_until_complete(self.libjuju.list_offers("model"))
1039 mock_disconnect_controller.assert_called_once()
1040
1041 def test_empty_list(
1042 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1043 ):
1044 mock_get_controller.return_value = juju.controller.Controller()
1045 mock_list_offers.return_value = []
1046 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
1047 self.assertEqual(offers, [])
1048 mock_disconnect_controller.assert_called_once()
1049
1050 def test_non_empty_list(
1051 self, mock_list_offers, mock_disconnect_controller, mock_get_controller,
1052 ):
1053 mock_get_controller.return_value = juju.controller.Controller()
1054 mock_list_offers.return_value = ["offer"]
1055 offers = self.loop.run_until_complete(self.libjuju.list_offers("model"))
1056 self.assertEqual(offers, ["offer"])
1057 mock_disconnect_controller.assert_called_once()
David Garcia68b00722020-09-11 15:05:00 +02001058
1059
1060@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1061@asynctest.mock.patch("juju.controller.Controller.get_model")
1062@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1063@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1064@asynctest.mock.patch("juju.model.Model.consume")
1065class ConsumeTest(LibjujuTestCase):
1066 def setUp(self):
1067 super(ConsumeTest, self).setUp()
1068
1069 def test_consume(
1070 self,
1071 mock_consume,
1072 mock_disconnect_controller,
1073 mock_disconnect_model,
1074 mock_get_model,
1075 mock_get_controller,
1076 ):
1077 mock_get_controller.return_value = juju.controller.Controller()
1078 mock_get_model.return_value = juju.model.Model()
1079
David Garcia12b29242020-09-17 16:01:48 +02001080 self.loop.run_until_complete(self.libjuju.consume("offer_url", "model_name"))
David Garcia68b00722020-09-11 15:05:00 +02001081 mock_consume.assert_called_once()
1082 mock_disconnect_model.assert_called_once()
1083 mock_disconnect_controller.assert_called_once()
1084
1085 def test_parsing_error_exception(
1086 self,
1087 mock_consume,
1088 mock_disconnect_controller,
1089 mock_disconnect_model,
1090 mock_get_model,
1091 mock_get_controller,
1092 ):
1093 mock_get_controller.return_value = juju.controller.Controller()
1094 mock_get_model.return_value = juju.model.Model()
1095 mock_consume.side_effect = juju.offerendpoints.ParseError("")
1096
1097 with self.assertRaises(juju.offerendpoints.ParseError):
1098 self.loop.run_until_complete(
1099 self.libjuju.consume("offer_url", "model_name")
1100 )
1101 mock_consume.assert_called_once()
1102 mock_disconnect_model.assert_called_once()
1103 mock_disconnect_controller.assert_called_once()
1104
1105 def test_juju_error_exception(
1106 self,
1107 mock_consume,
1108 mock_disconnect_controller,
1109 mock_disconnect_model,
1110 mock_get_model,
1111 mock_get_controller,
1112 ):
1113 mock_get_controller.return_value = juju.controller.Controller()
1114 mock_get_model.return_value = juju.model.Model()
1115 mock_consume.side_effect = juju.errors.JujuError("")
1116
1117 with self.assertRaises(juju.errors.JujuError):
1118 self.loop.run_until_complete(
1119 self.libjuju.consume("offer_url", "model_name")
1120 )
1121 mock_consume.assert_called_once()
1122 mock_disconnect_model.assert_called_once()
1123 mock_disconnect_controller.assert_called_once()
1124
1125 def test_juju_api_error_exception(
1126 self,
1127 mock_consume,
1128 mock_disconnect_controller,
1129 mock_disconnect_model,
1130 mock_get_model,
1131 mock_get_controller,
1132 ):
1133 mock_get_controller.return_value = juju.controller.Controller()
1134 mock_get_model.return_value = juju.model.Model()
David Garcia12b29242020-09-17 16:01:48 +02001135 mock_consume.side_effect = juju.errors.JujuAPIError(
1136 {"error": "", "response": "", "request-id": ""}
1137 )
David Garcia68b00722020-09-11 15:05:00 +02001138
1139 with self.assertRaises(juju.errors.JujuAPIError):
1140 self.loop.run_until_complete(
1141 self.libjuju.consume("offer_url", "model_name")
1142 )
1143 mock_consume.assert_called_once()
1144 mock_disconnect_model.assert_called_once()
1145 mock_disconnect_controller.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02001146
1147
David Garcia475a7222020-09-21 16:19:15 +02001148@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_k8s_cloud_credential")
David Garcia12b29242020-09-17 16:01:48 +02001149@asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
1150class AddK8sTest(LibjujuTestCase):
1151 def setUp(self):
1152 super(AddK8sTest, self).setUp()
David Garcia475a7222020-09-21 16:19:15 +02001153 self.configuration = kubernetes.client.configuration.Configuration()
David Garcia12b29242020-09-17 16:01:48 +02001154
David Garcia475a7222020-09-21 16:19:15 +02001155 def test_add_k8s(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02001156 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001157 self.libjuju.add_k8s("cloud", self.configuration, "storage_class")
David Garcia12b29242020-09-17 16:01:48 +02001158 )
1159 mock_add_cloud.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02001160 mock_get_k8s_cloud_credential.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02001161
David Garcia475a7222020-09-21 16:19:15 +02001162 def test_add_k8s_exception(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02001163 mock_add_cloud.side_effect = Exception()
1164 with self.assertRaises(Exception):
1165 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001166 self.libjuju.add_k8s("cloud", self.configuration, "storage_class")
David Garcia12b29242020-09-17 16:01:48 +02001167 )
1168 mock_add_cloud.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02001169 mock_get_k8s_cloud_credential.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02001170
David Garcia475a7222020-09-21 16:19:15 +02001171 def test_add_k8s_missing_name(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02001172 with self.assertRaises(Exception):
1173 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001174 self.libjuju.add_k8s("", self.configuration, "storage_class")
David Garcia12b29242020-09-17 16:01:48 +02001175 )
1176 mock_add_cloud.assert_not_called()
1177
David Garcia475a7222020-09-21 16:19:15 +02001178 def test_add_k8s_missing_storage_name(
1179 self, mock_add_cloud, mock_get_k8s_cloud_credential
1180 ):
David Garcia12b29242020-09-17 16:01:48 +02001181 with self.assertRaises(Exception):
1182 self.loop.run_until_complete(
David Garcia475a7222020-09-21 16:19:15 +02001183 self.libjuju.add_k8s("cloud", self.configuration, "")
David Garcia12b29242020-09-17 16:01:48 +02001184 )
1185 mock_add_cloud.assert_not_called()
1186
David Garcia475a7222020-09-21 16:19:15 +02001187 def test_add_k8s_missing_configuration_keys(
1188 self, mock_add_cloud, mock_get_k8s_cloud_credential
1189 ):
David Garcia12b29242020-09-17 16:01:48 +02001190 with self.assertRaises(Exception):
David Garcia475a7222020-09-21 16:19:15 +02001191 self.loop.run_until_complete(self.libjuju.add_k8s("cloud", None, ""))
David Garcia12b29242020-09-17 16:01:48 +02001192 mock_add_cloud.assert_not_called()
1193
1194
1195@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1196@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1197@asynctest.mock.patch("juju.controller.Controller.add_cloud")
1198@asynctest.mock.patch("juju.controller.Controller.add_credential")
1199class AddCloudTest(LibjujuTestCase):
1200 def setUp(self):
1201 super(AddCloudTest, self).setUp()
1202 self.cloud = juju.client.client.Cloud()
1203 self.credential = juju.client.client.CloudCredential()
1204
1205 def test_add_cloud_with_credential(
1206 self,
1207 mock_add_credential,
1208 mock_add_cloud,
1209 mock_disconnect_controller,
1210 mock_get_controller,
1211 ):
1212 mock_get_controller.return_value = juju.controller.Controller()
1213
1214 cloud = self.loop.run_until_complete(
1215 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1216 )
1217 self.assertEqual(cloud, self.cloud)
1218 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1219 mock_add_credential.assert_called_once_with(
1220 "cloud", credential=self.credential, cloud="cloud"
1221 )
1222 mock_disconnect_controller.assert_called_once()
1223
1224 def test_add_cloud_no_credential(
1225 self,
1226 mock_add_credential,
1227 mock_add_cloud,
1228 mock_disconnect_controller,
1229 mock_get_controller,
1230 ):
1231 mock_get_controller.return_value = juju.controller.Controller()
1232
1233 cloud = self.loop.run_until_complete(
1234 self.libjuju.add_cloud("cloud", self.cloud)
1235 )
1236 self.assertEqual(cloud, self.cloud)
1237 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1238 mock_add_credential.assert_not_called()
1239 mock_disconnect_controller.assert_called_once()
1240
1241 def test_add_cloud_exception(
1242 self,
1243 mock_add_credential,
1244 mock_add_cloud,
1245 mock_disconnect_controller,
1246 mock_get_controller,
1247 ):
1248 mock_get_controller.return_value = juju.controller.Controller()
1249 mock_add_cloud.side_effect = Exception()
1250 with self.assertRaises(Exception):
1251 self.loop.run_until_complete(
1252 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1253 )
1254
1255 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1256 mock_add_credential.assert_not_called()
1257 mock_disconnect_controller.assert_called_once()
1258
1259 def test_add_credential_exception(
1260 self,
1261 mock_add_credential,
1262 mock_add_cloud,
1263 mock_disconnect_controller,
1264 mock_get_controller,
1265 ):
1266 mock_get_controller.return_value = juju.controller.Controller()
1267 mock_add_credential.side_effect = Exception()
1268 with self.assertRaises(Exception):
1269 self.loop.run_until_complete(
1270 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
1271 )
1272
1273 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
1274 mock_add_credential.assert_called_once_with(
1275 "cloud", credential=self.credential, cloud="cloud"
1276 )
1277 mock_disconnect_controller.assert_called_once()
1278
1279
1280@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1281@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1282@asynctest.mock.patch("juju.controller.Controller.remove_cloud")
1283class RemoveCloudTest(LibjujuTestCase):
1284 def setUp(self):
1285 super(RemoveCloudTest, self).setUp()
1286
1287 def test_remove_cloud(
1288 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1289 ):
1290 mock_get_controller.return_value = juju.controller.Controller()
1291
1292 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1293 mock_remove_cloud.assert_called_once_with("cloud")
1294 mock_disconnect_controller.assert_called_once()
1295
1296 def test_remove_cloud_exception(
1297 self, mock_remove_cloud, mock_disconnect_controller, mock_get_controller,
1298 ):
1299 mock_get_controller.return_value = juju.controller.Controller()
1300 mock_remove_cloud.side_effect = Exception()
1301
1302 with self.assertRaises(Exception):
1303 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
1304 mock_remove_cloud.assert_called_once_with("cloud")
1305 mock_disconnect_controller.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02001306
1307
1308@asynctest.mock.patch("kubernetes.client.configuration.Configuration")
1309class GetK8sCloudCredentials(LibjujuTestCase):
1310 def setUp(self):
1311 super(GetK8sCloudCredentials, self).setUp()
1312
1313 @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
1314 def test_not_supported(self, mock_exception, mock_configuration):
1315 mock_configuration.username = ""
1316 mock_configuration.password = ""
1317 mock_configuration.ssl_ca_cert = None
1318 mock_configuration.cert_file = None
1319 mock_configuration.key_file = None
1320 exception_raised = False
1321 try:
1322 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1323 except JujuInvalidK8sConfiguration as e:
1324 exception_raised = True
1325 self.assertEqual(
1326 e.message, "authentication method not supported",
1327 )
1328 self.assertTrue(exception_raised)
1329
1330 def test_user_pass(self, mock_configuration):
1331 mock_configuration.username = "admin"
1332 mock_configuration.password = "admin"
1333 mock_configuration.ssl_ca_cert = None
1334 mock_configuration.cert_file = None
1335 mock_configuration.key_file = None
1336 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1337 self.assertEqual(
1338 credential,
1339 juju.client._definitions.CloudCredential(
1340 attrs={"username": "admin", "password": "admin"}, auth_type="userpass"
1341 ),
1342 )
1343
1344 def test_user_no_pass(self, mock_configuration):
1345 mock_configuration.username = "admin"
1346 mock_configuration.password = ""
1347 mock_configuration.ssl_ca_cert = None
1348 mock_configuration.cert_file = None
1349 mock_configuration.key_file = None
1350 with mock.patch.object(self.libjuju.log, "debug") as mock_debug:
1351 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1352 self.assertEqual(
1353 credential,
1354 juju.client._definitions.CloudCredential(
1355 attrs={"username": "admin", "password": ""}, auth_type="userpass"
1356 ),
1357 )
1358 mock_debug.assert_called_once_with(
1359 "credential for user admin has empty password"
1360 )
1361
1362 def test_user_pass_with_cert(self, mock_configuration):
1363 mock_configuration.username = "admin"
1364 mock_configuration.password = "admin"
1365 ssl_ca_cert = tempfile.NamedTemporaryFile()
1366 with open(ssl_ca_cert.name, "w") as ssl_ca_cert_file:
1367 ssl_ca_cert_file.write("cacert")
1368 mock_configuration.ssl_ca_cert = ssl_ca_cert.name
1369 mock_configuration.cert_file = None
1370 mock_configuration.key_file = None
1371 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1372 self.assertEqual(
1373 credential,
1374 juju.client._definitions.CloudCredential(
1375 attrs={
1376 "username": "admin",
1377 "password": "admin",
1378 "ClientCertificateData": "cacert",
1379 },
1380 auth_type="userpasswithcert",
1381 ),
1382 )
1383
1384 def test_cert(self, mock_configuration):
1385 mock_configuration.username = ""
1386 mock_configuration.password = ""
1387 mock_configuration.api_key = {"authorization": "Bearer Token"}
1388 ssl_ca_cert = tempfile.NamedTemporaryFile()
1389 with open(ssl_ca_cert.name, "w") as ssl_ca_cert_file:
1390 ssl_ca_cert_file.write("cacert")
1391 mock_configuration.ssl_ca_cert = ssl_ca_cert.name
1392 mock_configuration.cert_file = None
1393 mock_configuration.key_file = None
1394 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1395 self.assertEqual(
1396 credential,
1397 juju.client._definitions.CloudCredential(
1398 attrs={"ClientCertificateData": "cacert", "Token": "Token"},
1399 auth_type="certificate",
1400 ),
1401 )
1402
1403 def test_oauth2(self, mock_configuration):
1404 mock_configuration.username = ""
1405 mock_configuration.password = ""
1406 mock_configuration.api_key = {"authorization": "Bearer Token"}
1407 key = tempfile.NamedTemporaryFile()
1408 with open(key.name, "w") as key_file:
1409 key_file.write("key")
1410 mock_configuration.ssl_ca_cert = None
1411 mock_configuration.cert_file = None
1412 mock_configuration.key_file = key.name
1413 credential = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1414 self.assertEqual(
1415 credential,
1416 juju.client._definitions.CloudCredential(
1417 attrs={"ClientKeyData": "key", "Token": "Token"}, auth_type="oauth2",
1418 ),
1419 )
1420
1421 @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
1422 def test_oauth2_missing_token(self, mock_exception, mock_configuration):
1423 mock_configuration.username = ""
1424 mock_configuration.password = ""
1425 key = tempfile.NamedTemporaryFile()
1426 with open(key.name, "w") as key_file:
1427 key_file.write("key")
1428 mock_configuration.ssl_ca_cert = None
1429 mock_configuration.cert_file = None
1430 mock_configuration.key_file = key.name
1431 exception_raised = False
1432 try:
1433 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1434 except JujuInvalidK8sConfiguration as e:
1435 exception_raised = True
1436 self.assertEqual(
1437 e.message, "missing token for auth type oauth2",
1438 )
1439 self.assertTrue(exception_raised)
1440
1441 def test_unknown_api_key(self, mock_configuration):
1442 mock_configuration.username = ""
1443 mock_configuration.password = ""
1444 mock_configuration.api_key = {"authorization": "Bearer Token Wrong"}
1445 mock_configuration.ssl_ca_cert = None
1446 mock_configuration.cert_file = None
1447 mock_configuration.key_file = None
1448 exception_raised = False
1449 try:
1450 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1451 except JujuInvalidK8sConfiguration as e:
1452 exception_raised = True
1453 self.assertEqual(
1454 e.message, "unknown format of api_key",
1455 )
1456 self.assertTrue(exception_raised)
1457
1458 def test_exception_cannot_set_token_and_userpass(self, mock_configuration):
1459 mock_configuration.username = "admin"
1460 mock_configuration.password = "pass"
1461 mock_configuration.api_key = {"authorization": "No_bearer_token"}
1462 mock_configuration.ssl_ca_cert = None
1463 mock_configuration.cert_file = None
1464 mock_configuration.key_file = None
1465 exception_raised = False
1466 try:
1467 _ = self.libjuju.get_k8s_cloud_credential(mock_configuration)
1468 except JujuInvalidK8sConfiguration as e:
1469 exception_raised = True
1470 self.assertEqual(
1471 e.message, "Cannot set both token and user/pass",
1472 )
1473 self.assertTrue(exception_raised)