blob: 9f21bc668b49fa7747c16fb689e4323d19ec5525 [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
David Garciaeb8943a2021-04-12 12:07:37 +020018from unittest.mock import Mock, patch
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
David Garcia582b9232021-10-26 12:30:44 +020023
24from n2vc.definitions import Offer, RelationEndpoint
aktasfa02f8a2021-07-29 17:41:40 +030025from .utils import (
26 FakeApplication,
27 FakeMachine,
28 FakeManualMachine,
29 FakeUnit,
30)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020031from n2vc.libjuju import Libjuju
32from n2vc.exceptions import (
33 JujuControllerFailedConnecting,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020034 JujuMachineNotFound,
35 JujuApplicationNotFound,
36 JujuActionNotFound,
37 JujuApplicationExists,
David Garcia475a7222020-09-21 16:19:15 +020038 JujuInvalidK8sConfiguration,
David Garcia59f520d2020-10-15 13:16:45 +020039 JujuLeaderUnitNotFound,
ksaikiranrb84e4a72021-03-17 12:53:20 +053040 JujuError,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020041)
David Garciaf6e9b002020-11-27 15:32:02 +010042from n2vc.k8s_juju_conn import generate_rbac_id
David Garciaeb8943a2021-04-12 12:07:37 +020043from n2vc.tests.unit.utils import AsyncMock
44from n2vc.vca.connection import Connection
45from n2vc.vca.connection_data import ConnectionData
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020046
47
David Garciaeb8943a2021-04-12 12:07:37 +020048cacert = """-----BEGIN CERTIFICATE-----
49SOMECERT
50-----END CERTIFICATE-----"""
51
52
53@asynctest.mock.patch("n2vc.libjuju.Controller")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020054class LibjujuTestCase(asynctest.TestCase):
David Garciaeb8943a2021-04-12 12:07:37 +020055 @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020056 def setUp(
57 self,
David Garciaeb8943a2021-04-12 12:07:37 +020058 mock_base64_to_cacert=None,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020059 ):
David Garciaeb8943a2021-04-12 12:07:37 +020060 self.loop = asyncio.get_event_loop()
61 self.db = Mock()
62 mock_base64_to_cacert.return_value = cacert
David Garcia553d1b72022-06-21 11:26:57 +020063 # Connection._load_vca_connection_data = Mock()
David Garciaeb8943a2021-04-12 12:07:37 +020064 vca_connection = Connection(AsyncMock())
65 vca_connection._data = ConnectionData(
66 **{
67 "endpoints": ["1.2.3.4:17070"],
68 "user": "user",
69 "secret": "secret",
70 "cacert": "cacert",
71 "pubkey": "pubkey",
72 "lxd-cloud": "cloud",
73 "lxd-credentials": "credentials",
74 "k8s-cloud": "k8s_cloud",
75 "k8s-credentials": "k8s_credentials",
76 "model-config": {},
77 "api-proxy": "api_proxy",
78 }
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020079 )
80 logging.disable(logging.CRITICAL)
David Garciaeb8943a2021-04-12 12:07:37 +020081 self.libjuju = Libjuju(vca_connection, self.loop)
82 self.loop.run_until_complete(self.libjuju.disconnect())
David Garciaa4f57d62020-10-22 10:50:56 +020083
84
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020085@asynctest.mock.patch("juju.controller.Controller.connect")
86@asynctest.mock.patch(
87 "juju.controller.Controller.api_endpoints",
88 new_callable=asynctest.CoroutineMock(return_value=["127.0.0.1:17070"]),
89)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020090class GetControllerTest(LibjujuTestCase):
91 def setUp(self):
92 super(GetControllerTest, self).setUp()
93
David Garciaeb8943a2021-04-12 12:07:37 +020094 def test_diff_endpoint(self, mock_api_endpoints, mock_connect):
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020095 self.libjuju.endpoints = []
96 controller = self.loop.run_until_complete(self.libjuju.get_controller())
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +020097 self.assertIsInstance(controller, juju.controller.Controller)
98
99 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
100 def test_exception(
101 self,
102 mock_disconnect_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200103 mock_api_endpoints,
104 mock_connect,
105 ):
106 self.libjuju.endpoints = []
David Garciaeb8943a2021-04-12 12:07:37 +0200107
108 mock_connect.side_effect = Exception()
David Garcia12b29242020-09-17 16:01:48 +0200109 controller = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200110 with self.assertRaises(JujuControllerFailedConnecting):
111 controller = self.loop.run_until_complete(self.libjuju.get_controller())
David Garcia12b29242020-09-17 16:01:48 +0200112 self.assertIsNone(controller)
David Garcia4ae527e2021-07-26 16:04:59 +0200113 mock_disconnect_controller.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200114
David Garciaeb8943a2021-04-12 12:07:37 +0200115 def test_same_endpoint_get_controller(self, mock_api_endpoints, mock_connect):
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200116 self.libjuju.endpoints = ["127.0.0.1:17070"]
117 controller = self.loop.run_until_complete(self.libjuju.get_controller())
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200118 self.assertIsInstance(controller, juju.controller.Controller)
119
120
121class DisconnectTest(LibjujuTestCase):
122 def setUp(self):
123 super(DisconnectTest, self).setUp()
124
125 @asynctest.mock.patch("juju.model.Model.disconnect")
126 def test_disconnect_model(self, mock_disconnect):
127 self.loop.run_until_complete(self.libjuju.disconnect_model(juju.model.Model()))
128 mock_disconnect.assert_called_once()
129
130 @asynctest.mock.patch("juju.controller.Controller.disconnect")
131 def test_disconnect_controller(self, mock_disconnect):
132 self.loop.run_until_complete(
133 self.libjuju.disconnect_controller(juju.controller.Controller())
134 )
135 mock_disconnect.assert_called_once()
136
137
138@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
139@asynctest.mock.patch("n2vc.libjuju.Libjuju.model_exists")
140@asynctest.mock.patch("juju.controller.Controller.add_model")
141@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
142@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
143class AddModelTest(LibjujuTestCase):
144 def setUp(self):
145 super(AddModelTest, self).setUp()
146
147 def test_existing_model(
148 self,
149 mock_disconnect_model,
150 mock_disconnect_controller,
151 mock_add_model,
152 mock_model_exists,
153 mock_get_controller,
154 ):
155 mock_model_exists.return_value = True
156
David Garciab0a8f402021-03-15 18:41:34 +0100157 # This should not raise an exception
David Garciaeb8943a2021-04-12 12:07:37 +0200158 self.loop.run_until_complete(self.libjuju.add_model("existing_model", "cloud"))
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200159
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(
David Garciaeb8943a2021-04-12 12:07:37 +0200176 self.libjuju.add_model("nonexisting_model", Mock())
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200177 )
178
179 mock_add_model.assert_called_once()
180 mock_disconnect_controller.assert_called()
181 mock_disconnect_model.assert_called()
182
183
ksaikiranrb84e4a72021-03-17 12:53:20 +0530184@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
185@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
186@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
187@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
188@asynctest.mock.patch(
189 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
190)
191@asynctest.mock.patch("juju.model.Model.get_action_status")
192@asynctest.mock.patch("juju.model.Model.get_action_output")
193@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_actions")
194class GetExecutedActionsTest(LibjujuTestCase):
195 def setUp(self):
196 super(GetExecutedActionsTest, self).setUp()
197
198 def test_exception(
199 self,
200 mock_get_actions,
201 mock_get_action_output,
202 mock_get_action_status,
203 mock_applications,
204 mock_disconnect_controller,
205 mock_disconnect_model,
206 mock_get_model,
207 mock_get_controller,
208 ):
209 mock_get_model.return_value = None
210 with self.assertRaises(JujuError):
211 self.loop.run_until_complete(self.libjuju.get_executed_actions("model"))
212
213 mock_get_controller.assert_called_once()
214 mock_disconnect_controller.assert_called_once()
215 mock_get_model.assert_called_once()
216 mock_disconnect_model.assert_not_called()
217
218 def test_success(
219 self,
220 mock_get_actions,
221 mock_get_action_output,
222 mock_get_action_status,
223 mock_applications,
224 mock_disconnect_controller,
225 mock_disconnect_model,
226 mock_get_model,
227 mock_get_controller,
228 ):
229 mock_get_model.return_value = juju.model.Model()
230 mock_applications.return_value = {"existing_app"}
231 mock_get_actions.return_value = {"action_name": "description"}
232 mock_get_action_status.return_value = {"id": "status"}
233 mock_get_action_output.return_value = {"output": "completed"}
234
235 executed_actions = self.loop.run_until_complete(
236 self.libjuju.get_executed_actions("model")
237 )
garciadeblas82b591c2021-03-24 09:22:13 +0100238 expected_result = [
239 {
240 "id": "id",
241 "action": "action_name",
242 "status": "status",
243 "output": "completed",
244 }
245 ]
ksaikiranrb84e4a72021-03-17 12:53:20 +0530246 self.assertListEqual(expected_result, executed_actions)
247 self.assertIsInstance(executed_actions, list)
248
249 mock_get_controller.assert_called_once()
250 mock_get_model.assert_called_once()
251 mock_disconnect_controller.assert_called_once()
252 mock_disconnect_model.assert_called_once()
253
254
255@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
256@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
257@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
258@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
259@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
260class GetApplicationConfigsTest(LibjujuTestCase):
261 def setUp(self):
262 super(GetApplicationConfigsTest, self).setUp()
263
264 def test_exception(
265 self,
266 mock_get_application,
267 mock_disconnect_controller,
268 mock_disconnect_model,
269 mock_get_model,
270 mock_get_controller,
271 ):
272 mock_get_model.return_value = None
273 with self.assertRaises(JujuError):
274 self.loop.run_until_complete(
garciadeblas82b591c2021-03-24 09:22:13 +0100275 self.libjuju.get_application_configs("model", "app")
276 )
ksaikiranrb84e4a72021-03-17 12:53:20 +0530277
278 mock_get_controller.assert_called_once()
279 mock_disconnect_controller.assert_called_once()
280 mock_get_model.assert_called_once()
281 mock_disconnect_model.assert_not_called()
282
283 def test_success(
284 self,
285 mock_get_application,
286 mock_disconnect_controller,
287 mock_disconnect_model,
288 mock_get_model,
289 mock_get_controller,
290 ):
291 mock_get_application.return_value = FakeApplication()
garciadeblas82b591c2021-03-24 09:22:13 +0100292 application_configs = self.loop.run_until_complete(
293 self.libjuju.get_application_configs("model", "app")
294 )
ksaikiranrb84e4a72021-03-17 12:53:20 +0530295
296 self.assertEqual(application_configs, ["app_config"])
297
298 mock_get_controller.assert_called_once()
299 mock_get_model.assert_called_once()
300 mock_disconnect_controller.assert_called_once()
301 mock_disconnect_model.assert_called_once()
302
303
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200304@asynctest.mock.patch("juju.controller.Controller.get_model")
305class GetModelTest(LibjujuTestCase):
306 def setUp(self):
307 super(GetModelTest, self).setUp()
308
309 def test_get_model(
David Garcia5b802c92020-11-11 16:56:06 +0100310 self,
311 mock_get_model,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200312 ):
313 mock_get_model.return_value = juju.model.Model()
314 model = self.loop.run_until_complete(
315 self.libjuju.get_model(juju.controller.Controller(), "model")
316 )
317 self.assertIsInstance(model, juju.model.Model)
318
319
320@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
321@asynctest.mock.patch("juju.controller.Controller.list_models")
322class ModelExistsTest(LibjujuTestCase):
323 def setUp(self):
324 super(ModelExistsTest, self).setUp()
325
326 async def test_existing_model(
David Garcia5b802c92020-11-11 16:56:06 +0100327 self,
328 mock_list_models,
329 mock_get_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200330 ):
331 mock_list_models.return_value = ["existing_model"]
332 self.assertTrue(
333 await self.libjuju.model_exists(
334 "existing_model", juju.controller.Controller()
335 )
336 )
337
338 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
339 async def test_no_controller(
David Garcia5b802c92020-11-11 16:56:06 +0100340 self,
341 mock_disconnect_controller,
342 mock_list_models,
343 mock_get_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200344 ):
345 mock_list_models.return_value = ["existing_model"]
346 mock_get_controller.return_value = juju.controller.Controller()
347 self.assertTrue(await self.libjuju.model_exists("existing_model"))
348 mock_disconnect_controller.assert_called_once()
349
350 async def test_non_existing_model(
David Garcia5b802c92020-11-11 16:56:06 +0100351 self,
352 mock_list_models,
353 mock_get_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200354 ):
355 mock_list_models.return_value = ["existing_model"]
356 self.assertFalse(
357 await self.libjuju.model_exists(
358 "not_existing_model", juju.controller.Controller()
359 )
360 )
361
362
363@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
364@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
365@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
366@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
367@asynctest.mock.patch("juju.model.Model.get_status")
368class GetModelStatusTest(LibjujuTestCase):
369 def setUp(self):
370 super(GetModelStatusTest, self).setUp()
371
372 def test_success(
373 self,
374 mock_get_status,
375 mock_disconnect_controller,
376 mock_disconnect_model,
377 mock_get_model,
378 mock_get_controller,
379 ):
380 mock_get_model.return_value = juju.model.Model()
381 mock_get_status.return_value = {"status"}
382
383 status = self.loop.run_until_complete(self.libjuju.get_model_status("model"))
384
385 mock_get_status.assert_called_once()
386 mock_disconnect_controller.assert_called_once()
387 mock_disconnect_model.assert_called_once()
388
389 self.assertEqual(status, {"status"})
390
David Garcia12b29242020-09-17 16:01:48 +0200391 def test_exception(
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200392 self,
393 mock_get_status,
394 mock_disconnect_controller,
395 mock_disconnect_model,
396 mock_get_model,
397 mock_get_controller,
398 ):
399 mock_get_model.return_value = juju.model.Model()
400 mock_get_status.side_effect = Exception()
David Garcia12b29242020-09-17 16:01:48 +0200401 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200402 with self.assertRaises(Exception):
403 status = self.loop.run_until_complete(
404 self.libjuju.get_model_status("model")
405 )
406
David Garcia12b29242020-09-17 16:01:48 +0200407 mock_disconnect_controller.assert_called_once()
408 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200409
David Garcia12b29242020-09-17 16:01:48 +0200410 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200411
412
413@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
414@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
415@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
416@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
417@asynctest.mock.patch("juju.model.Model.get_machines")
418@asynctest.mock.patch("juju.model.Model.add_machine")
419@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
420class CreateMachineTest(LibjujuTestCase):
421 def setUp(self):
422 super(CreateMachineTest, self).setUp()
423
424 def test_existing_machine(
425 self,
426 mock_wait_for,
427 mock_add_machine,
428 mock_get_machines,
429 mock_disconnect_controller,
430 mock_disconnect_model,
431 mock_get_model,
432 mock_get_controller,
433 ):
434 mock_get_model.return_value = juju.model.Model()
435 mock_get_machines.return_value = {"existing_machine": FakeMachine()}
436 machine, bool_res = self.loop.run_until_complete(
437 self.libjuju.create_machine("model", "existing_machine")
438 )
439
440 self.assertIsInstance(machine, FakeMachine)
441 self.assertFalse(bool_res)
442
443 mock_disconnect_controller.assert_called()
444 mock_disconnect_model.assert_called()
445
446 def test_non_existing_machine(
447 self,
448 mock_wait_for,
449 mock_add_machine,
450 mock_get_machines,
451 mock_disconnect_controller,
452 mock_disconnect_model,
453 mock_get_model,
454 mock_get_controller,
455 ):
David Garcia12b29242020-09-17 16:01:48 +0200456 machine = None
457 bool_res = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200458 mock_get_model.return_value = juju.model.Model()
459 with self.assertRaises(JujuMachineNotFound):
460 machine, bool_res = self.loop.run_until_complete(
461 self.libjuju.create_machine("model", "non_existing_machine")
462 )
David Garcia12b29242020-09-17 16:01:48 +0200463 self.assertIsNone(machine)
464 self.assertIsNone(bool_res)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200465
David Garcia12b29242020-09-17 16:01:48 +0200466 mock_disconnect_controller.assert_called()
467 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200468
469 def test_no_machine(
470 self,
471 mock_wait_for,
472 mock_add_machine,
473 mock_get_machines,
474 mock_disconnect_controller,
475 mock_disconnect_model,
476 mock_get_model,
477 mock_get_controller,
478 ):
479 mock_get_model.return_value = juju.model.Model()
480 mock_add_machine.return_value = FakeMachine()
481
482 machine, bool_res = self.loop.run_until_complete(
483 self.libjuju.create_machine("model")
484 )
485
486 self.assertIsInstance(machine, FakeMachine)
487 self.assertTrue(bool_res)
488
489 mock_wait_for.assert_called_once()
490 mock_add_machine.assert_called_once()
491
492 mock_disconnect_controller.assert_called()
493 mock_disconnect_model.assert_called()
494
495
496# TODO test provision machine
497
498
Patricia Reinosofedf9152023-01-17 08:39:44 +0000499@asynctest.mock.patch("os.remove")
500@asynctest.mock.patch("n2vc.libjuju.yaml.dump")
501@asynctest.mock.patch("builtins.open", create=True)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200502@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
503@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
504@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
505@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
David Garcia667696e2020-09-22 14:52:32 +0200506@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for_model")
507@asynctest.mock.patch("juju.model.Model.deploy")
Patricia Reinosofedf9152023-01-17 08:39:44 +0000508@asynctest.mock.patch("juju.model.CharmhubDeployType.resolve")
509@asynctest.mock.patch("n2vc.libjuju.BundleHandler")
510@asynctest.mock.patch("juju.url.URL.parse")
David Garcia667696e2020-09-22 14:52:32 +0200511class DeployTest(LibjujuTestCase):
512 def setUp(self):
513 super(DeployTest, self).setUp()
Patricia Reinosofedf9152023-01-17 08:39:44 +0000514 self.instantiation_params = {"applications": {"squid": {"scale": 2}}}
515 self.architecture = "amd64"
516 self.uri = "cs:osm"
517 self.url = AsyncMock()
518 self.url.schema = juju.url.Schema.CHARM_HUB
519 self.bundle_instance = None
520
521 def setup_bundle_download_mocks(
522 self, mock_url_parse, mock_bundle, mock_resolve, mock_get_model
523 ):
524 mock_url_parse.return_value = self.url
525 mock_bundle.return_value = AsyncMock()
526 mock_resolve.return_value = AsyncMock()
527 mock_resolve.origin = AsyncMock()
528 mock_get_model.return_value = juju.model.Model()
529 self.bundle_instance = mock_bundle.return_value
530 self.bundle_instance.applications = {"squid"}
531
532 def assert_overlay_file_is_written(self, filename, mocked_file, mock_yaml, mock_os):
533 mocked_file.assert_called_once_with(filename, "w")
534 mock_yaml.assert_called_once_with(
535 self.instantiation_params, mocked_file.return_value.__enter__.return_value
536 )
537 mock_os.assert_called_once_with(filename)
538
539 def assert_overlay_file_is_not_written(self, mocked_file, mock_yaml, mock_os):
540 mocked_file.assert_not_called()
541 mock_yaml.assert_not_called()
542 mock_os.assert_not_called()
543
544 def assert_bundle_is_downloaded(self, mock_resolve, mock_url_parse):
545 mock_resolve.assert_called_once_with(
546 self.url, self.architecture, entity_url=self.uri
547 )
548 mock_url_parse.assert_called_once_with(self.uri)
549 self.bundle_instance.fetch_plan.assert_called_once_with(
550 self.url, mock_resolve.origin
551 )
552
553 def assert_bundle_is_not_downloaded(self, mock_resolve, mock_url_parse):
554 mock_resolve.assert_not_called()
555 mock_url_parse.assert_not_called()
556 self.bundle_instance.fetch_plan.assert_not_called()
David Garcia667696e2020-09-22 14:52:32 +0200557
558 def test_deploy(
559 self,
Patricia Reinosofedf9152023-01-17 08:39:44 +0000560 mock_url_parse,
561 mock_bundle,
562 mock_resolve,
David Garcia667696e2020-09-22 14:52:32 +0200563 mock_deploy,
564 mock_wait_for_model,
565 mock_disconnect_controller,
566 mock_disconnect_model,
567 mock_get_model,
568 mock_get_controller,
Patricia Reinosofedf9152023-01-17 08:39:44 +0000569 mocked_file,
570 mock_yaml,
571 mock_os,
David Garcia667696e2020-09-22 14:52:32 +0200572 ):
Patricia Reinosofedf9152023-01-17 08:39:44 +0000573 self.setup_bundle_download_mocks(
574 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
David Garcia667696e2020-09-22 14:52:32 +0200575 )
Patricia Reinosofedf9152023-01-17 08:39:44 +0000576 model_name = "model1"
577
578 self.loop.run_until_complete(
579 self.libjuju.deploy(
580 "cs:osm",
581 model_name,
582 wait=True,
583 timeout=0,
584 instantiation_params=None,
585 )
586 )
587 self.assert_overlay_file_is_not_written(mocked_file, mock_yaml, mock_os)
588 self.assert_bundle_is_not_downloaded(mock_resolve, mock_url_parse)
589 mock_deploy.assert_called_once_with("cs:osm", trust=True, overlays=[])
David Garcia667696e2020-09-22 14:52:32 +0200590 mock_wait_for_model.assert_called_once()
591 mock_disconnect_controller.assert_called_once()
592 mock_disconnect_model.assert_called_once()
593
594 def test_deploy_no_wait(
595 self,
Patricia Reinosofedf9152023-01-17 08:39:44 +0000596 mock_url_parse,
597 mock_bundle,
598 mock_resolve,
David Garcia667696e2020-09-22 14:52:32 +0200599 mock_deploy,
600 mock_wait_for_model,
601 mock_disconnect_controller,
602 mock_disconnect_model,
603 mock_get_model,
604 mock_get_controller,
Patricia Reinosofedf9152023-01-17 08:39:44 +0000605 mocked_file,
606 mock_yaml,
607 mock_os,
David Garcia667696e2020-09-22 14:52:32 +0200608 ):
Patricia Reinosofedf9152023-01-17 08:39:44 +0000609 self.setup_bundle_download_mocks(
610 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
David Garcia667696e2020-09-22 14:52:32 +0200611 )
Patricia Reinosofedf9152023-01-17 08:39:44 +0000612 self.loop.run_until_complete(
613 self.libjuju.deploy(
614 "cs:osm", "model", wait=False, timeout=0, instantiation_params={}
615 )
616 )
617 self.assert_overlay_file_is_not_written(mocked_file, mock_yaml, mock_os)
618 self.assert_bundle_is_not_downloaded(mock_resolve, mock_url_parse)
619 mock_deploy.assert_called_once_with("cs:osm", trust=True, overlays=[])
David Garcia667696e2020-09-22 14:52:32 +0200620 mock_wait_for_model.assert_not_called()
621 mock_disconnect_controller.assert_called_once()
622 mock_disconnect_model.assert_called_once()
623
624 def test_deploy_exception(
625 self,
Patricia Reinosofedf9152023-01-17 08:39:44 +0000626 mock_url_parse,
627 mock_bundle,
628 mock_resolve,
David Garcia667696e2020-09-22 14:52:32 +0200629 mock_deploy,
630 mock_wait_for_model,
631 mock_disconnect_controller,
632 mock_disconnect_model,
633 mock_get_model,
634 mock_get_controller,
Patricia Reinosofedf9152023-01-17 08:39:44 +0000635 mocked_file,
636 mock_yaml,
637 mock_os,
David Garcia667696e2020-09-22 14:52:32 +0200638 ):
Patricia Reinosofedf9152023-01-17 08:39:44 +0000639 self.setup_bundle_download_mocks(
640 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
641 )
David Garcia667696e2020-09-22 14:52:32 +0200642 mock_deploy.side_effect = Exception()
David Garcia667696e2020-09-22 14:52:32 +0200643 with self.assertRaises(Exception):
644 self.loop.run_until_complete(self.libjuju.deploy("cs:osm", "model"))
Patricia Reinosofedf9152023-01-17 08:39:44 +0000645 self.assert_overlay_file_is_not_written(mocked_file, mock_yaml, mock_os)
646 self.assert_bundle_is_not_downloaded(mock_resolve, mock_url_parse)
David Garcia667696e2020-09-22 14:52:32 +0200647 mock_deploy.assert_called_once()
648 mock_wait_for_model.assert_not_called()
649 mock_disconnect_controller.assert_called_once()
650 mock_disconnect_model.assert_called_once()
651
Patricia Reinosofedf9152023-01-17 08:39:44 +0000652 def test_deploy_with_instantiation_params(
653 self,
654 mock_url_parse,
655 mock_bundle,
656 mock_resolve,
657 mock_deploy,
658 mock_wait_for_model,
659 mock_disconnect_controller,
660 mock_disconnect_model,
661 mock_get_model,
662 mock_get_controller,
663 mocked_file,
664 mock_yaml,
665 mock_os,
666 ):
667 self.setup_bundle_download_mocks(
668 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
669 )
670 model_name = "model1"
671 expected_filename = "{}-overlay.yaml".format(model_name)
672 self.loop.run_until_complete(
673 self.libjuju.deploy(
674 self.uri,
675 model_name,
676 wait=True,
677 timeout=0,
678 instantiation_params=self.instantiation_params,
679 )
680 )
681 self.assert_overlay_file_is_written(
682 expected_filename, mocked_file, mock_yaml, mock_os
683 )
684 self.assert_bundle_is_downloaded(mock_resolve, mock_url_parse)
685 mock_deploy.assert_called_once_with(
686 self.uri, trust=True, overlays=[expected_filename]
687 )
688 mock_wait_for_model.assert_called_once()
689 mock_disconnect_controller.assert_called_once()
690 mock_disconnect_model.assert_called_once()
691
692 def test_deploy_with_instantiation_params_no_applications(
693 self,
694 mock_url_parse,
695 mock_bundle,
696 mock_resolve,
697 mock_deploy,
698 mock_wait_for_model,
699 mock_disconnect_controller,
700 mock_disconnect_model,
701 mock_get_model,
702 mock_get_controller,
703 mocked_file,
704 mock_yaml,
705 mock_os,
706 ):
707 self.instantiation_params = {"applications": {}}
708 self.setup_bundle_download_mocks(
709 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
710 )
711
712 model_name = "model3"
713 expected_filename = "{}-overlay.yaml".format(model_name)
714 self.loop.run_until_complete(
715 self.libjuju.deploy(
716 self.uri,
717 model_name,
718 wait=False,
719 timeout=0,
720 instantiation_params=self.instantiation_params,
721 )
722 )
723
724 self.assert_overlay_file_is_written(
725 expected_filename, mocked_file, mock_yaml, mock_os
726 )
727 self.assert_bundle_is_not_downloaded(mock_resolve, mock_url_parse)
728 mock_deploy.assert_called_once_with(
729 self.uri, trust=True, overlays=[expected_filename]
730 )
731 mock_wait_for_model.assert_not_called()
732 mock_disconnect_controller.assert_called_once()
733 mock_disconnect_model.assert_called_once()
734
735 def test_deploy_with_instantiation_params_applications_not_found(
736 self,
737 mock_url_parse,
738 mock_bundle,
739 mock_resolve,
740 mock_deploy,
741 mock_wait_for_model,
742 mock_disconnect_controller,
743 mock_disconnect_model,
744 mock_get_model,
745 mock_get_controller,
746 mocked_file,
747 mock_yaml,
748 mock_os,
749 ):
750 self.instantiation_params = {"some_key": {"squid": {"scale": 2}}}
751 self.setup_bundle_download_mocks(
752 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
753 )
754
755 with self.assertRaises(JujuError):
756 self.loop.run_until_complete(
757 self.libjuju.deploy(
758 self.uri,
759 "model1",
760 wait=True,
761 timeout=0,
762 instantiation_params=self.instantiation_params,
763 )
764 )
765
766 self.assert_overlay_file_is_not_written(mocked_file, mock_yaml, mock_os)
767 self.assert_bundle_is_not_downloaded(mock_resolve, mock_url_parse)
768 mock_deploy.assert_not_called()
769 mock_wait_for_model.assert_not_called()
770 mock_disconnect_controller.assert_called_once()
771 mock_disconnect_model.assert_called_once()
772
773 def test_deploy_overlay_contains_invalid_app(
774 self,
775 mock_url_parse,
776 mock_bundle,
777 mock_resolve,
778 mock_deploy,
779 mock_wait_for_model,
780 mock_disconnect_controller,
781 mock_disconnect_model,
782 mock_get_model,
783 mock_get_controller,
784 mocked_file,
785 mock_yaml,
786 mock_os,
787 ):
788 self.setup_bundle_download_mocks(
789 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
790 )
791 self.bundle_instance.applications = {"new_app"}
792
793 with self.assertRaises(JujuApplicationNotFound) as error:
794 self.loop.run_until_complete(
795 self.libjuju.deploy(
796 self.uri,
797 "model2",
798 wait=True,
799 timeout=0,
800 instantiation_params=self.instantiation_params,
801 )
802 )
803 error_msg = "Cannot find application ['squid'] in original bundle {'new_app'}"
804 self.assertEqual(str(error.exception), error_msg)
805
806 self.assert_overlay_file_is_not_written(mocked_file, mock_yaml, mock_os)
807 self.assert_bundle_is_downloaded(mock_resolve, mock_url_parse)
808 mock_deploy.assert_not_called()
809 mock_wait_for_model.assert_not_called()
810 mock_disconnect_controller.assert_called_once()
811 mock_disconnect_model.assert_called_once()
812
813 def test_deploy_exception_with_instantiation_params(
814 self,
815 mock_url_parse,
816 mock_bundle,
817 mock_resolve,
818 mock_deploy,
819 mock_wait_for_model,
820 mock_disconnect_controller,
821 mock_disconnect_model,
822 mock_get_model,
823 mock_get_controller,
824 mocked_file,
825 mock_yaml,
826 mock_os,
827 ):
828 self.setup_bundle_download_mocks(
829 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
830 )
831
832 mock_deploy.side_effect = Exception()
833 model_name = "model2"
834 expected_filename = "{}-overlay.yaml".format(model_name)
835 with self.assertRaises(Exception):
836 self.loop.run_until_complete(
837 self.libjuju.deploy(
838 self.uri,
839 model_name,
840 instantiation_params=self.instantiation_params,
841 )
842 )
843
844 self.assert_overlay_file_is_written(
845 expected_filename, mocked_file, mock_yaml, mock_os
846 )
847 self.assert_bundle_is_downloaded(mock_resolve, mock_url_parse)
848 mock_deploy.assert_called_once_with(
849 self.uri, trust=True, overlays=[expected_filename]
850 )
851 mock_wait_for_model.assert_not_called()
852 mock_disconnect_controller.assert_called_once()
853 mock_disconnect_model.assert_called_once()
854
855 @asynctest.mock.patch("logging.Logger.warning")
856 def test_deploy_exception_when_deleting_file_is_not_propagated(
857 self,
858 mock_warning,
859 mock_url_parse,
860 mock_bundle,
861 mock_resolve,
862 mock_deploy,
863 mock_wait_for_model,
864 mock_disconnect_controller,
865 mock_disconnect_model,
866 mock_get_model,
867 mock_get_controller,
868 mocked_file,
869 mock_yaml,
870 mock_os,
871 ):
872 self.setup_bundle_download_mocks(
873 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
874 )
875
876 mock_os.side_effect = OSError("Error")
877 model_name = "model2"
878 expected_filename = "{}-overlay.yaml".format(model_name)
879 self.loop.run_until_complete(
880 self.libjuju.deploy(
881 self.uri,
882 model_name,
883 instantiation_params=self.instantiation_params,
884 )
885 )
886
887 self.assert_overlay_file_is_written(
888 expected_filename, mocked_file, mock_yaml, mock_os
889 )
890 self.assert_bundle_is_downloaded(mock_resolve, mock_url_parse)
891 mock_deploy.assert_called_once_with(
892 self.uri, trust=True, overlays=[expected_filename]
893 )
894 mock_wait_for_model.assert_called_once()
895 mock_disconnect_controller.assert_called_once()
896 mock_disconnect_model.assert_called_once()
897 mock_warning.assert_called_with(
898 "Overlay file {} could not be removed: Error".format(expected_filename)
899 )
900
David Garcia667696e2020-09-22 14:52:32 +0200901
902@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
903@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
904@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
905@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200906@asynctest.mock.patch(
907 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
908)
909@asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
910@asynctest.mock.patch("juju.model.Model.deploy")
911@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
912@asynctest.mock.patch("n2vc.libjuju.Libjuju.create_machine")
913class DeployCharmTest(LibjujuTestCase):
914 def setUp(self):
915 super(DeployCharmTest, self).setUp()
916
917 def test_existing_app(
918 self,
919 mock_create_machine,
920 mock_wait_for,
921 mock_deploy,
922 mock_machines,
923 mock_applications,
924 mock_disconnect_controller,
925 mock_disconnect_model,
926 mock_get_model,
927 mock_get_controller,
928 ):
929 mock_get_model.return_value = juju.model.Model()
930 mock_applications.return_value = {"existing_app"}
931
David Garcia12b29242020-09-17 16:01:48 +0200932 application = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200933 with self.assertRaises(JujuApplicationExists):
934 application = self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +0100935 self.libjuju.deploy_charm(
936 "existing_app",
937 "path",
938 "model",
939 "machine",
940 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200941 )
David Garcia12b29242020-09-17 16:01:48 +0200942 self.assertIsNone(application)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200943
David Garcia12b29242020-09-17 16:01:48 +0200944 mock_disconnect_controller.assert_called()
945 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200946
947 def test_non_existing_machine(
948 self,
949 mock_create_machine,
950 mock_wait_for,
951 mock_deploy,
952 mock_machines,
953 mock_applications,
954 mock_disconnect_controller,
955 mock_disconnect_model,
956 mock_get_model,
957 mock_get_controller,
958 ):
959 mock_get_model.return_value = juju.model.Model()
960 mock_machines.return_value = {"existing_machine": FakeMachine()}
David Garcia12b29242020-09-17 16:01:48 +0200961 application = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200962 with self.assertRaises(JujuMachineNotFound):
963 application = self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +0100964 self.libjuju.deploy_charm(
965 "app",
966 "path",
967 "model",
968 "machine",
969 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200970 )
971
David Garcia12b29242020-09-17 16:01:48 +0200972 self.assertIsNone(application)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200973
David Garcia12b29242020-09-17 16:01:48 +0200974 mock_disconnect_controller.assert_called()
975 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +0200976
977 def test_2_units(
978 self,
979 mock_create_machine,
980 mock_wait_for,
981 mock_deploy,
982 mock_machines,
983 mock_applications,
984 mock_disconnect_controller,
985 mock_disconnect_model,
986 mock_get_model,
987 mock_get_controller,
988 ):
989 mock_get_model.return_value = juju.model.Model()
990 mock_machines.return_value = {"existing_machine": FakeMachine()}
991 mock_create_machine.return_value = (FakeMachine(), "other")
992 mock_deploy.return_value = FakeApplication()
993 application = self.loop.run_until_complete(
994 self.libjuju.deploy_charm(
David Garcia5b802c92020-11-11 16:56:06 +0100995 "app",
996 "path",
997 "model",
998 "existing_machine",
999 num_units=2,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001000 )
1001 )
1002
1003 self.assertIsInstance(application, FakeApplication)
1004
1005 mock_deploy.assert_called_once()
1006 mock_wait_for.assert_called_once()
1007
1008 mock_create_machine.assert_called_once()
1009
1010 mock_disconnect_controller.assert_called()
1011 mock_disconnect_model.assert_called()
1012
1013 def test_1_unit(
1014 self,
1015 mock_create_machine,
1016 mock_wait_for,
1017 mock_deploy,
1018 mock_machines,
1019 mock_applications,
1020 mock_disconnect_controller,
1021 mock_disconnect_model,
1022 mock_get_model,
1023 mock_get_controller,
1024 ):
1025 mock_get_model.return_value = juju.model.Model()
1026 mock_machines.return_value = {"existing_machine": FakeMachine()}
1027 mock_deploy.return_value = FakeApplication()
1028 application = self.loop.run_until_complete(
1029 self.libjuju.deploy_charm("app", "path", "model", "existing_machine")
1030 )
1031
1032 self.assertIsInstance(application, FakeApplication)
1033
1034 mock_deploy.assert_called_once()
1035 mock_wait_for.assert_called_once()
1036
1037 mock_disconnect_controller.assert_called()
1038 mock_disconnect_model.assert_called()
1039
1040
1041@asynctest.mock.patch(
1042 "juju.model.Model.applications", new_callable=asynctest.PropertyMock
1043)
1044class GetApplicationTest(LibjujuTestCase):
1045 def setUp(self):
1046 super(GetApplicationTest, self).setUp()
1047
1048 def test_existing_application(
David Garcia5b802c92020-11-11 16:56:06 +01001049 self,
1050 mock_applications,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001051 ):
1052 mock_applications.return_value = {"existing_app": "exists"}
1053 model = juju.model.Model()
1054 result = self.libjuju._get_application(model, "existing_app")
1055 self.assertEqual(result, "exists")
1056
1057 def test_non_existing_application(
David Garcia5b802c92020-11-11 16:56:06 +01001058 self,
1059 mock_applications,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001060 ):
1061 mock_applications.return_value = {"existing_app": "exists"}
1062 model = juju.model.Model()
1063 result = self.libjuju._get_application(model, "nonexisting_app")
1064 self.assertIsNone(result)
1065
1066
1067@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1068@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1069@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1070@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1071@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1072@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
1073@asynctest.mock.patch("juju.model.Model.get_action_output")
1074@asynctest.mock.patch("juju.model.Model.get_action_status")
1075class ExecuteActionTest(LibjujuTestCase):
1076 def setUp(self):
1077 super(ExecuteActionTest, self).setUp()
1078
1079 def test_no_application(
1080 self,
1081 mock_get_action_status,
1082 mock_get_action_output,
1083 mock_wait_for,
1084 mock__get_application,
1085 mock_disconnect_controller,
1086 mock_disconnect_model,
1087 mock_get_model,
1088 mock_get_controller,
1089 ):
1090 mock__get_application.return_value = None
1091 mock_get_model.return_value = juju.model.Model()
David Garcia12b29242020-09-17 16:01:48 +02001092 output = None
1093 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001094 with self.assertRaises(JujuApplicationNotFound):
1095 output, status = self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001096 self.libjuju.execute_action(
1097 "app",
1098 "model",
1099 "action",
1100 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001101 )
David Garcia12b29242020-09-17 16:01:48 +02001102 self.assertIsNone(output)
1103 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001104
David Garcia12b29242020-09-17 16:01:48 +02001105 mock_disconnect_controller.assert_called()
1106 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001107
1108 def test_no_action(
1109 self,
1110 mock_get_action_status,
1111 mock_get_action_output,
1112 mock_wait_for,
1113 mock__get_application,
1114 mock_disconnect_controller,
1115 mock_disconnect_model,
1116 mock_get_model,
1117 mock_get_controller,
1118 ):
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001119 mock_get_model.return_value = juju.model.Model()
1120 mock__get_application.return_value = FakeApplication()
David Garcia12b29242020-09-17 16:01:48 +02001121 output = None
1122 status = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001123 with self.assertRaises(JujuActionNotFound):
1124 output, status = self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001125 self.libjuju.execute_action(
1126 "app",
1127 "model",
1128 "action",
1129 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001130 )
David Garcia12b29242020-09-17 16:01:48 +02001131 self.assertIsNone(output)
1132 self.assertIsNone(status)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001133
David Garcia12b29242020-09-17 16:01:48 +02001134 mock_disconnect_controller.assert_called()
1135 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001136
David Garcia59f520d2020-10-15 13:16:45 +02001137 @asynctest.mock.patch("asyncio.sleep")
1138 @asynctest.mock.patch("n2vc.tests.unit.utils.FakeUnit.is_leader_from_status")
1139 def test_no_leader(
1140 self,
1141 mock_is_leader_from_status,
1142 mock_sleep,
1143 mock_get_action_status,
1144 mock_get_action_output,
1145 mock_wait_for,
1146 mock__get_application,
1147 mock_disconnect_controller,
1148 mock_disconnect_model,
1149 mock_get_model,
1150 mock_get_controller,
1151 ):
1152 mock_get_model.return_value = juju.model.Model()
1153 mock__get_application.return_value = FakeApplication()
1154 mock_is_leader_from_status.return_value = False
1155 output = None
1156 status = None
1157 with self.assertRaises(JujuLeaderUnitNotFound):
1158 output, status = self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001159 self.libjuju.execute_action(
1160 "app",
1161 "model",
1162 "action",
1163 )
David Garcia59f520d2020-10-15 13:16:45 +02001164 )
1165 self.assertIsNone(output)
1166 self.assertIsNone(status)
1167
1168 mock_disconnect_controller.assert_called()
1169 mock_disconnect_model.assert_called()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001170
aktasfa02f8a2021-07-29 17:41:40 +03001171 def test_successful_exec(
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001172 self,
1173 mock_get_action_status,
1174 mock_get_action_output,
1175 mock_wait_for,
1176 mock__get_application,
1177 mock_disconnect_controller,
1178 mock_disconnect_model,
1179 mock_get_model,
1180 mock_get_controller,
1181 ):
1182 mock_get_model.return_value = juju.model.Model()
1183 mock__get_application.return_value = FakeApplication()
1184 mock_get_action_output.return_value = "output"
1185 mock_get_action_status.return_value = {"id": "status"}
1186 output, status = self.loop.run_until_complete(
1187 self.libjuju.execute_action("app", "model", "existing_action")
1188 )
1189 self.assertEqual(output, "output")
1190 self.assertEqual(status, "status")
1191
1192 mock_wait_for.assert_called_once()
1193
1194 mock_disconnect_controller.assert_called()
1195 mock_disconnect_model.assert_called()
1196
1197
1198@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1199@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1200@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1201@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1202@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1203class GetActionTest(LibjujuTestCase):
1204 def setUp(self):
1205 super(GetActionTest, self).setUp()
1206
1207 def test_exception(
1208 self,
1209 mock_get_application,
1210 mock_disconnect_controller,
1211 mock_disconnect_model,
1212 mock_get_model,
1213 mock_get_controller,
1214 ):
1215 mock_get_application.side_effect = Exception()
David Garcia12b29242020-09-17 16:01:48 +02001216 actions = None
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001217 with self.assertRaises(Exception):
1218 actions = self.loop.run_until_complete(
1219 self.libjuju.get_actions("app", "model")
1220 )
1221
David Garcia12b29242020-09-17 16:01:48 +02001222 self.assertIsNone(actions)
1223 mock_disconnect_controller.assert_called_once()
1224 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001225
1226 def test_success(
1227 self,
1228 mock_get_application,
1229 mock_disconnect_controller,
1230 mock_disconnect_model,
1231 mock_get_model,
1232 mock_get_controller,
1233 ):
1234 mock_get_application.return_value = FakeApplication()
1235
1236 actions = self.loop.run_until_complete(self.libjuju.get_actions("app", "model"))
1237
1238 self.assertEqual(actions, ["existing_action"])
1239
1240 mock_get_controller.assert_called_once()
1241 mock_get_model.assert_called_once()
1242 mock_disconnect_controller.assert_called_once()
1243 mock_disconnect_model.assert_called_once()
1244
1245
1246@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1247@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1248@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1249@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
David Garcia85755d12020-09-21 19:51:23 +02001250@asynctest.mock.patch("juju.application.Application.get_metrics")
1251@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1252class GetMetricsTest(LibjujuTestCase):
1253 def setUp(self):
1254 super(GetMetricsTest, self).setUp()
1255
1256 def test_get_metrics_success(
1257 self,
1258 mock_get_application,
1259 mock_get_metrics,
1260 mock_disconnect_controller,
1261 mock_disconnect_model,
1262 mock_get_model,
1263 mock_get_controller,
1264 ):
1265 mock_get_application.return_value = FakeApplication()
1266 mock_get_model.return_value = juju.model.Model()
1267
1268 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
1269
1270 mock_disconnect_controller.assert_called_once()
1271 mock_disconnect_model.assert_called_once()
1272
1273 def test_get_metrics_exception(
1274 self,
1275 mock_get_application,
1276 mock_get_metrics,
1277 mock_disconnect_controller,
1278 mock_disconnect_model,
1279 mock_get_model,
1280 mock_get_controller,
1281 ):
1282 mock_get_model.return_value = juju.model.Model()
1283 mock_get_metrics.side_effect = Exception()
1284 with self.assertRaises(Exception):
1285 self.loop.run_until_complete(self.libjuju.get_metrics("model", "app1"))
1286
1287 mock_disconnect_controller.assert_called_once()
1288 mock_disconnect_model.assert_called_once()
1289
1290 def test_missing_args_exception(
1291 self,
1292 mock_get_application,
1293 mock_get_metrics,
1294 mock_disconnect_controller,
1295 mock_disconnect_model,
1296 mock_get_model,
1297 mock_get_controller,
1298 ):
1299 mock_get_model.return_value = juju.model.Model()
1300
1301 with self.assertRaises(Exception):
1302 self.loop.run_until_complete(self.libjuju.get_metrics("", ""))
1303
1304 mock_get_controller.assert_not_called()
1305 mock_get_model.assert_not_called()
1306 mock_disconnect_controller.assert_not_called()
1307 mock_disconnect_model.assert_not_called()
1308
1309
1310@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1311@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1312@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1313@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001314@asynctest.mock.patch("juju.model.Model.add_relation")
1315class AddRelationTest(LibjujuTestCase):
1316 def setUp(self):
1317 super(AddRelationTest, self).setUp()
1318
1319 @asynctest.mock.patch("logging.Logger.warning")
1320 def test_not_found(
1321 self,
1322 mock_warning,
1323 mock_add_relation,
1324 mock_disconnect_controller,
1325 mock_disconnect_model,
1326 mock_get_model,
1327 mock_get_controller,
1328 ):
1329 # TODO in libjuju.py should this fail only with a log message?
1330 result = {"error": "not found", "response": "response", "request-id": 1}
1331
1332 mock_get_model.return_value = juju.model.Model()
1333 mock_add_relation.side_effect = JujuAPIError(result)
1334
1335 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001336 self.libjuju.add_relation(
1337 "model",
1338 "app1:relation1",
1339 "app2:relation2",
1340 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001341 )
1342
1343 mock_warning.assert_called_with("Relation not found: not found")
1344 mock_disconnect_controller.assert_called_once()
1345 mock_disconnect_model.assert_called_once()
1346
1347 @asynctest.mock.patch("logging.Logger.warning")
Patricia Reinosoa07f6952023-01-04 10:40:10 +00001348 def test_not_found_in_error_code(
1349 self,
1350 mock_warning,
1351 mock_add_relation,
1352 mock_disconnect_controller,
1353 mock_disconnect_model,
1354 mock_get_model,
1355 mock_get_controller,
1356 ):
1357 result = {
1358 "error": "relation cannot be added",
1359 "error-code": "not found",
1360 "response": "response",
1361 "request-id": 1,
1362 }
1363
1364 mock_get_model.return_value = juju.model.Model()
1365 mock_add_relation.side_effect = JujuAPIError(result)
1366
1367 self.loop.run_until_complete(
1368 self.libjuju.add_relation(
1369 "model",
1370 "app1:relation1",
1371 "app2:relation2",
1372 )
1373 )
1374
1375 mock_warning.assert_called_with("Relation not found: relation cannot be added")
1376 mock_disconnect_controller.assert_called_once()
1377 mock_disconnect_model.assert_called_once()
1378
1379 @asynctest.mock.patch("logging.Logger.warning")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001380 def test_already_exists(
1381 self,
1382 mock_warning,
1383 mock_add_relation,
1384 mock_disconnect_controller,
1385 mock_disconnect_model,
1386 mock_get_model,
1387 mock_get_controller,
1388 ):
1389 # TODO in libjuju.py should this fail silently?
1390 result = {"error": "already exists", "response": "response", "request-id": 1}
1391
1392 mock_get_model.return_value = juju.model.Model()
1393 mock_add_relation.side_effect = JujuAPIError(result)
1394
1395 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001396 self.libjuju.add_relation(
1397 "model",
1398 "app1:relation1",
1399 "app2:relation2",
1400 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001401 )
1402
1403 mock_warning.assert_called_with("Relation already exists: already exists")
1404 mock_disconnect_controller.assert_called_once()
1405 mock_disconnect_model.assert_called_once()
1406
Patricia Reinosoa07f6952023-01-04 10:40:10 +00001407 @asynctest.mock.patch("logging.Logger.warning")
1408 def test_already_exists_error_code(
1409 self,
1410 mock_warning,
1411 mock_add_relation,
1412 mock_disconnect_controller,
1413 mock_disconnect_model,
1414 mock_get_model,
1415 mock_get_controller,
1416 ):
1417 result = {
1418 "error": "relation cannot be added",
1419 "error-code": "already exists",
1420 "response": "response",
1421 "request-id": 1,
1422 }
1423
1424 mock_get_model.return_value = juju.model.Model()
1425 mock_add_relation.side_effect = JujuAPIError(result)
1426
1427 self.loop.run_until_complete(
1428 self.libjuju.add_relation(
1429 "model",
1430 "app1:relation1",
1431 "app2:relation2",
1432 )
1433 )
1434
1435 mock_warning.assert_called_with(
1436 "Relation already exists: relation cannot be added"
1437 )
1438 mock_disconnect_controller.assert_called_once()
1439 mock_disconnect_model.assert_called_once()
1440
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001441 def test_exception(
1442 self,
1443 mock_add_relation,
1444 mock_disconnect_controller,
1445 mock_disconnect_model,
1446 mock_get_model,
1447 mock_get_controller,
1448 ):
1449 mock_get_model.return_value = juju.model.Model()
1450 result = {"error": "", "response": "response", "request-id": 1}
1451 mock_add_relation.side_effect = JujuAPIError(result)
1452
1453 with self.assertRaises(JujuAPIError):
1454 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001455 self.libjuju.add_relation(
1456 "model",
1457 "app1:relation1",
1458 "app2:relation2",
1459 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001460 )
1461
David Garcia12b29242020-09-17 16:01:48 +02001462 mock_disconnect_controller.assert_called_once()
1463 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001464
1465 def test_success(
1466 self,
1467 mock_add_relation,
1468 mock_disconnect_controller,
1469 mock_disconnect_model,
1470 mock_get_model,
1471 mock_get_controller,
1472 ):
1473 mock_get_model.return_value = juju.model.Model()
1474
1475 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001476 self.libjuju.add_relation(
1477 "model",
1478 "app1:relation1",
1479 "app2:relation2",
1480 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001481 )
1482
David Garcia12b29242020-09-17 16:01:48 +02001483 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
David Garcia8331f7c2020-08-25 16:10:07 +02001484 mock_disconnect_controller.assert_called_once()
1485 mock_disconnect_model.assert_called_once()
1486
1487 def test_saas(
1488 self,
1489 mock_add_relation,
1490 mock_disconnect_controller,
1491 mock_disconnect_model,
1492 mock_get_model,
1493 mock_get_controller,
1494 ):
1495 mock_get_model.return_value = juju.model.Model()
1496
1497 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001498 self.libjuju.add_relation(
1499 "model",
1500 "app1:relation1",
1501 "saas_name",
1502 )
David Garcia8331f7c2020-08-25 16:10:07 +02001503 )
1504
David Garcia12b29242020-09-17 16:01:48 +02001505 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001506 mock_disconnect_controller.assert_called_once()
1507 mock_disconnect_model.assert_called_once()
1508
1509
1510# TODO destroy_model testcase
1511
1512
aktas56120292021-02-26 15:32:39 +03001513@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1514@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1515@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1516@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1517@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1518class DestroyApplicationTest(LibjujuTestCase):
1519 def setUp(self):
1520 super(DestroyApplicationTest, self).setUp()
1521
1522 def test_success(
garciadeblas82b591c2021-03-24 09:22:13 +01001523 self,
1524 mock_get_controller,
1525 mock_get_model,
1526 mock_disconnect_controller,
1527 mock_get_application,
1528 mock_disconnect_model,
aktas56120292021-02-26 15:32:39 +03001529 ):
1530 mock_get_application.return_value = FakeApplication()
1531 mock_get_model.return_value = None
1532 self.loop.run_until_complete(
1533 self.libjuju.destroy_application(
1534 "existing_model",
1535 "existing_app",
1536 3600,
1537 )
1538 )
1539 mock_get_application.assert_called()
1540 mock_disconnect_controller.assert_called_once()
1541 mock_disconnect_model.assert_called_once()
1542
1543 def test_no_application(
garciadeblas82b591c2021-03-24 09:22:13 +01001544 self,
1545 mock_get_controller,
1546 mock_get_model,
1547 mock_disconnect_controller,
1548 mock_get_application,
1549 mock_disconnect_model,
aktas56120292021-02-26 15:32:39 +03001550 ):
1551 mock_get_model.return_value = None
1552 mock_get_application.return_value = None
1553
1554 self.loop.run_until_complete(
1555 self.libjuju.destroy_application(
1556 "existing_model",
1557 "existing_app",
1558 3600,
1559 )
1560 )
1561 mock_get_application.assert_called()
1562
1563 def test_exception(
garciadeblas82b591c2021-03-24 09:22:13 +01001564 self,
1565 mock_get_controller,
1566 mock_get_model,
1567 mock_disconnect_controller,
1568 mock_get_application,
1569 mock_disconnect_model,
aktas56120292021-02-26 15:32:39 +03001570 ):
1571 mock_get_application.return_value = FakeApplication
1572 mock_get_model.return_value = None
1573
1574 with self.assertRaises(Exception):
1575 self.loop.run_until_complete(
1576 self.libjuju.destroy_application(
1577 "existing_model",
1578 "existing_app",
1579 0,
1580 )
1581 )
1582 mock_get_application.assert_called_once()
1583
1584
David Garcia5ef42a12020-09-29 19:48:13 +02001585# @asynctest.mock.patch("juju.model.Model.get_machines")
1586# @asynctest.mock.patch("logging.Logger.debug")
1587# class DestroyMachineTest(LibjujuTestCase):
1588# def setUp(self):
1589# super(DestroyMachineTest, self).setUp()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001590
David Garcia5ef42a12020-09-29 19:48:13 +02001591# def test_success_manual_machine(
1592# self, mock_debug, mock_get_machines,
1593# ):
1594# mock_get_machines.side_effect = [
1595# {"machine": FakeManualMachine()},
1596# {"machine": FakeManualMachine()},
1597# {},
1598# ]
1599# self.loop.run_until_complete(
1600# self.libjuju.destroy_machine(juju.model.Model(), "machine", 2,)
1601# )
1602# calls = [
1603# asynctest.call("Waiting for machine machine is destroyed"),
1604# asynctest.call("Machine destroyed: machine"),
1605# ]
1606# mock_debug.assert_has_calls(calls)
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001607
David Garcia5ef42a12020-09-29 19:48:13 +02001608# def test_no_machine(
1609# self, mock_debug, mock_get_machines,
1610# ):
1611# mock_get_machines.return_value = {}
1612# self.loop.run_until_complete(
1613# self.libjuju.destroy_machine(juju.model.Model(), "machine", 2)
1614# )
1615# mock_debug.assert_called_with("Machine not found: machine")
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001616
1617
1618@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1619@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
1620@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1621@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1622@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1623class ConfigureApplicationTest(LibjujuTestCase):
1624 def setUp(self):
1625 super(ConfigureApplicationTest, self).setUp()
1626
1627 def test_success(
1628 self,
1629 mock_get_application,
1630 mock_disconnect_controller,
1631 mock_disconnect_model,
1632 mock_get_model,
1633 mock_get_controller,
1634 ):
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001635 mock_get_application.return_value = FakeApplication()
1636
1637 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001638 self.libjuju.configure_application(
1639 "model",
1640 "app",
1641 {"config"},
1642 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001643 )
1644 mock_get_application.assert_called_once()
1645 mock_disconnect_controller.assert_called_once()
1646 mock_disconnect_model.assert_called_once()
1647
1648 def test_exception(
1649 self,
1650 mock_get_application,
1651 mock_disconnect_controller,
1652 mock_disconnect_model,
1653 mock_get_model,
1654 mock_get_controller,
1655 ):
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001656 mock_get_application.side_effect = Exception()
1657
1658 with self.assertRaises(Exception):
1659 self.loop.run_until_complete(
David Garcia5b802c92020-11-11 16:56:06 +01001660 self.libjuju.configure_application(
1661 "model",
1662 "app",
1663 {"config"},
1664 )
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001665 )
David Garcia12b29242020-09-17 16:01:48 +02001666 mock_disconnect_controller.assert_called_once()
1667 mock_disconnect_model.assert_called_once()
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001668
David Garcia5b802c92020-11-11 16:56:06 +01001669 def test_controller_exception(
1670 self,
1671 mock_get_application,
1672 mock_disconnect_controller,
1673 mock_disconnect_model,
1674 mock_get_model,
1675 mock_get_controller,
1676 ):
David Garcia5b802c92020-11-11 16:56:06 +01001677 result = {"error": "not found", "response": "response", "request-id": 1}
1678
1679 mock_get_controller.side_effect = JujuAPIError(result)
1680
1681 with self.assertRaises(JujuAPIError):
1682 self.loop.run_until_complete(
1683 self.libjuju.configure_application(
1684 "model",
1685 "app",
1686 {"config"},
1687 )
1688 )
1689 mock_get_model.assert_not_called()
1690 mock_disconnect_controller.assert_not_called()
1691 mock_disconnect_model.assert_not_called()
1692
1693 def test_get_model_exception(
1694 self,
1695 mock_get_application,
1696 mock_disconnect_controller,
1697 mock_disconnect_model,
1698 mock_get_model,
1699 mock_get_controller,
1700 ):
David Garcia5b802c92020-11-11 16:56:06 +01001701 result = {"error": "not found", "response": "response", "request-id": 1}
1702 mock_get_model.side_effect = JujuAPIError(result)
1703
1704 with self.assertRaises(JujuAPIError):
1705 self.loop.run_until_complete(
1706 self.libjuju.configure_application(
1707 "model",
1708 "app",
1709 {"config"},
1710 )
1711 )
1712 mock_get_model.assert_called_once()
1713 mock_disconnect_controller.assert_called_once()
1714 mock_disconnect_model.assert_not_called()
1715
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001716
1717# TODO _get_api_endpoints_db test case
1718# TODO _update_api_endpoints_db test case
1719# TODO healthcheck test case
1720
1721
1722@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1723@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1724@asynctest.mock.patch("juju.controller.Controller.list_models")
1725class ListModelsTest(LibjujuTestCase):
1726 def setUp(self):
1727 super(ListModelsTest, self).setUp()
1728
1729 def test_containing(
David Garcia5b802c92020-11-11 16:56:06 +01001730 self,
1731 mock_list_models,
1732 mock_disconnect_controller,
1733 mock_get_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001734 ):
1735 mock_get_controller.return_value = juju.controller.Controller()
1736 mock_list_models.return_value = ["existingmodel"]
1737 models = self.loop.run_until_complete(self.libjuju.list_models("existing"))
1738
1739 mock_disconnect_controller.assert_called_once()
1740 self.assertEquals(models, ["existingmodel"])
1741
1742 def test_not_containing(
David Garcia5b802c92020-11-11 16:56:06 +01001743 self,
1744 mock_list_models,
1745 mock_disconnect_controller,
1746 mock_get_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001747 ):
1748 mock_get_controller.return_value = juju.controller.Controller()
1749 mock_list_models.return_value = ["existingmodel", "model"]
1750 models = self.loop.run_until_complete(self.libjuju.list_models("mdl"))
1751
1752 mock_disconnect_controller.assert_called_once()
1753 self.assertEquals(models, [])
1754
1755 def test_no_contains_arg(
David Garcia5b802c92020-11-11 16:56:06 +01001756 self,
1757 mock_list_models,
1758 mock_disconnect_controller,
1759 mock_get_controller,
Dominik Fleischmann7ff392f2020-07-07 13:11:19 +02001760 ):
1761 mock_get_controller.return_value = juju.controller.Controller()
1762 mock_list_models.return_value = ["existingmodel", "model"]
1763 models = self.loop.run_until_complete(self.libjuju.list_models())
1764
1765 mock_disconnect_controller.assert_called_once()
1766 self.assertEquals(models, ["existingmodel", "model"])
David Garcia42f328a2020-08-25 15:03:01 +02001767
1768
1769@asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
1770class ModelsExistTest(LibjujuTestCase):
1771 def setUp(self):
1772 super(ModelsExistTest, self).setUp()
1773
1774 def test_model_names_none(self, mock_list_models):
1775 mock_list_models.return_value = []
1776 with self.assertRaises(Exception):
1777 self.loop.run_until_complete(self.libjuju.models_exist(None))
1778
1779 def test_model_names_empty(self, mock_list_models):
1780 mock_list_models.return_value = []
1781 with self.assertRaises(Exception):
1782 (exist, non_existing_models) = self.loop.run_until_complete(
1783 self.libjuju.models_exist([])
1784 )
1785
1786 def test_model_names_not_existing(self, mock_list_models):
1787 mock_list_models.return_value = ["prometheus", "grafana"]
1788 (exist, non_existing_models) = self.loop.run_until_complete(
1789 self.libjuju.models_exist(["prometheus2", "grafana"])
1790 )
1791 self.assertFalse(exist)
1792 self.assertEqual(non_existing_models, ["prometheus2"])
1793
1794 def test_model_names_exist(self, mock_list_models):
1795 mock_list_models.return_value = ["prometheus", "grafana"]
1796 (exist, non_existing_models) = self.loop.run_until_complete(
1797 self.libjuju.models_exist(["prometheus", "grafana"])
1798 )
1799 self.assertTrue(exist)
1800 self.assertEqual(non_existing_models, [])
David Garciabc538e42020-08-25 15:22:30 +02001801
1802
1803@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1804@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1805@asynctest.mock.patch("juju.controller.Controller.list_offers")
1806class ListOffers(LibjujuTestCase):
1807 def setUp(self):
1808 super(ListOffers, self).setUp()
1809
1810 def test_disconnect_controller(
David Garcia5b802c92020-11-11 16:56:06 +01001811 self,
1812 mock_list_offers,
1813 mock_disconnect_controller,
1814 mock_get_controller,
David Garciabc538e42020-08-25 15:22:30 +02001815 ):
1816 mock_get_controller.return_value = juju.controller.Controller()
1817 mock_list_offers.side_effect = Exception()
1818 with self.assertRaises(Exception):
David Garcia582b9232021-10-26 12:30:44 +02001819 self.loop.run_until_complete(self.libjuju._list_offers("model"))
David Garciabc538e42020-08-25 15:22:30 +02001820 mock_disconnect_controller.assert_called_once()
1821
1822 def test_empty_list(
David Garcia5b802c92020-11-11 16:56:06 +01001823 self,
1824 mock_list_offers,
1825 mock_disconnect_controller,
1826 mock_get_controller,
David Garciabc538e42020-08-25 15:22:30 +02001827 ):
1828 mock_get_controller.return_value = juju.controller.Controller()
David Garcia582b9232021-10-26 12:30:44 +02001829 offer_results = Mock()
1830 offer_results.results = []
1831 mock_list_offers.return_value = offer_results
1832 offers = self.loop.run_until_complete(self.libjuju._list_offers("model"))
David Garciabc538e42020-08-25 15:22:30 +02001833 self.assertEqual(offers, [])
1834 mock_disconnect_controller.assert_called_once()
1835
1836 def test_non_empty_list(
David Garcia5b802c92020-11-11 16:56:06 +01001837 self,
1838 mock_list_offers,
1839 mock_disconnect_controller,
1840 mock_get_controller,
David Garciabc538e42020-08-25 15:22:30 +02001841 ):
1842 mock_get_controller.return_value = juju.controller.Controller()
David Garcia582b9232021-10-26 12:30:44 +02001843 offer = Mock()
1844 offer_results = Mock()
1845 offer_results.results = [offer]
1846 mock_list_offers.return_value = offer_results
1847 offers = self.loop.run_until_complete(self.libjuju._list_offers("model"))
1848 self.assertEqual(offers, [offer])
David Garciabc538e42020-08-25 15:22:30 +02001849 mock_disconnect_controller.assert_called_once()
David Garcia68b00722020-09-11 15:05:00 +02001850
David Garcia582b9232021-10-26 12:30:44 +02001851 def test_matching_offer_name(
1852 self,
1853 mock_list_offers,
1854 mock_disconnect_controller,
1855 mock_get_controller,
1856 ):
1857 mock_get_controller.return_value = juju.controller.Controller()
1858 offer_1 = Mock()
1859 offer_1.offer_name = "offer1"
1860 offer_2 = Mock()
1861 offer_2.offer_name = "offer2"
1862 offer_results = Mock()
1863 offer_results.results = [offer_1, offer_2]
1864 mock_list_offers.return_value = offer_results
1865 offers = self.loop.run_until_complete(
1866 self.libjuju._list_offers("model", offer_name="offer2")
1867 )
1868 self.assertEqual(offers, [offer_2])
1869 mock_disconnect_controller.assert_called_once()
1870
1871 def test_not_matching_offer_name(
1872 self,
1873 mock_list_offers,
1874 mock_disconnect_controller,
1875 mock_get_controller,
1876 ):
1877 mock_get_controller.return_value = juju.controller.Controller()
1878 offer_1 = Mock()
1879 offer_1.offer_name = "offer1"
1880 offer_2 = Mock()
1881 offer_2.offer_name = "offer2"
1882 offer_results = Mock()
1883 offer_results.results = [offer_1, offer_2]
1884 mock_list_offers.return_value = offer_results
1885 offers = self.loop.run_until_complete(
1886 self.libjuju._list_offers("model", offer_name="offer3")
1887 )
1888 self.assertEqual(offers, [])
1889 mock_disconnect_controller.assert_called_once()
1890
1891
1892@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1893@asynctest.mock.patch("juju.controller.Controller.get_model")
1894@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1895@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1896@asynctest.mock.patch("n2vc.libjuju.Libjuju._list_offers")
1897@asynctest.mock.patch("juju.model.Model.create_offer")
1898class OfferTest(LibjujuTestCase):
1899 def setUp(self):
1900 super(OfferTest, self).setUp()
1901
1902 def test_offer(
1903 self,
1904 mock_create_offer,
1905 mock__list_offers,
1906 mock_disconnect_controller,
1907 mock_disconnect_model,
1908 mock_get_model,
1909 mock_get_controller,
1910 ):
1911 controller = juju.controller.Controller()
1912 model = juju.model.Model()
1913 mock_get_controller.return_value = controller
1914 mock_get_model.return_value = model
1915 endpoint = RelationEndpoint("model.app-name.0", "vca", "endpoint")
1916 self.loop.run_until_complete(self.libjuju.offer(endpoint))
1917 mock_create_offer.assert_called_with(
1918 "app-name:endpoint", offer_name="app-name-endpoint"
1919 )
1920 mock_disconnect_model.assert_called_once_with(model)
1921 mock_disconnect_controller.assert_called_once_with(controller)
1922
1923 def test_offer_exception(
1924 self,
1925 mock_create_offer,
1926 mock__list_offers,
1927 mock_disconnect_controller,
1928 mock_disconnect_model,
1929 mock_get_model,
1930 mock_get_controller,
1931 ):
1932 controller = juju.controller.Controller()
1933 model = juju.model.Model()
1934 mock_get_controller.return_value = controller
1935 mock_get_model.return_value = model
1936 mock__list_offers.return_value = []
1937 endpoint = RelationEndpoint("model.app-name.0", "vca", "endpoint")
1938 with self.assertRaises(Exception):
1939 self.loop.run_until_complete(self.libjuju.offer(endpoint))
1940 mock_create_offer.assert_called_with(
1941 "app-name:endpoint", offer_name="app-name-endpoint"
1942 )
1943 mock_disconnect_model.assert_called_once_with(model)
1944 mock_disconnect_controller.assert_called_once_with(controller)
1945
David Garcia68b00722020-09-11 15:05:00 +02001946
1947@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
1948@asynctest.mock.patch("juju.controller.Controller.get_model")
1949@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
1950@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
1951@asynctest.mock.patch("juju.model.Model.consume")
1952class ConsumeTest(LibjujuTestCase):
1953 def setUp(self):
David Garcia582b9232021-10-26 12:30:44 +02001954 self.offer_url = "admin/model.offer_name"
David Garcia68b00722020-09-11 15:05:00 +02001955 super(ConsumeTest, self).setUp()
David Garcia582b9232021-10-26 12:30:44 +02001956 self.provider_libjuju = self.libjuju
David Garcia68b00722020-09-11 15:05:00 +02001957
1958 def test_consume(
1959 self,
1960 mock_consume,
1961 mock_disconnect_controller,
1962 mock_disconnect_model,
1963 mock_get_model,
1964 mock_get_controller,
1965 ):
David Garcia582b9232021-10-26 12:30:44 +02001966 self_controller = juju.controller.Controller()
1967 provider_controller = juju.controller.Controller()
1968 mock_get_controller.side_effect = [self_controller, provider_controller]
David Garcia68b00722020-09-11 15:05:00 +02001969 mock_get_model.return_value = juju.model.Model()
1970
David Garcia582b9232021-10-26 12:30:44 +02001971 self.loop.run_until_complete(
1972 self.libjuju.consume(
1973 "model_name",
1974 Offer(self.offer_url, vca_id="vca-id"),
1975 self.provider_libjuju,
1976 )
1977 )
1978 mock_consume.assert_called_once_with(
1979 "admin/model.offer_name",
1980 application_alias="offer_name-model-vca-id",
1981 controller=provider_controller,
1982 )
David Garcia68b00722020-09-11 15:05:00 +02001983 mock_disconnect_model.assert_called_once()
David Garcia582b9232021-10-26 12:30:44 +02001984 self.assertEqual(mock_disconnect_controller.call_count, 2)
David Garcia68b00722020-09-11 15:05:00 +02001985
1986 def test_parsing_error_exception(
1987 self,
1988 mock_consume,
1989 mock_disconnect_controller,
1990 mock_disconnect_model,
1991 mock_get_model,
1992 mock_get_controller,
1993 ):
1994 mock_get_controller.return_value = juju.controller.Controller()
1995 mock_get_model.return_value = juju.model.Model()
1996 mock_consume.side_effect = juju.offerendpoints.ParseError("")
1997
1998 with self.assertRaises(juju.offerendpoints.ParseError):
1999 self.loop.run_until_complete(
David Garcia582b9232021-10-26 12:30:44 +02002000 self.libjuju.consume(
2001 "model_name", Offer(self.offer_url), self.provider_libjuju
2002 )
David Garcia68b00722020-09-11 15:05:00 +02002003 )
2004 mock_consume.assert_called_once()
2005 mock_disconnect_model.assert_called_once()
David Garcia582b9232021-10-26 12:30:44 +02002006 self.assertEqual(mock_disconnect_controller.call_count, 2)
David Garcia68b00722020-09-11 15:05:00 +02002007
2008 def test_juju_error_exception(
2009 self,
2010 mock_consume,
2011 mock_disconnect_controller,
2012 mock_disconnect_model,
2013 mock_get_model,
2014 mock_get_controller,
2015 ):
2016 mock_get_controller.return_value = juju.controller.Controller()
2017 mock_get_model.return_value = juju.model.Model()
2018 mock_consume.side_effect = juju.errors.JujuError("")
2019
2020 with self.assertRaises(juju.errors.JujuError):
2021 self.loop.run_until_complete(
David Garcia582b9232021-10-26 12:30:44 +02002022 self.libjuju.consume(
2023 "model_name", Offer(self.offer_url), self.provider_libjuju
2024 )
David Garcia68b00722020-09-11 15:05:00 +02002025 )
2026 mock_consume.assert_called_once()
2027 mock_disconnect_model.assert_called_once()
David Garcia582b9232021-10-26 12:30:44 +02002028 self.assertEqual(mock_disconnect_controller.call_count, 2)
David Garcia68b00722020-09-11 15:05:00 +02002029
2030 def test_juju_api_error_exception(
2031 self,
2032 mock_consume,
2033 mock_disconnect_controller,
2034 mock_disconnect_model,
2035 mock_get_model,
2036 mock_get_controller,
2037 ):
2038 mock_get_controller.return_value = juju.controller.Controller()
2039 mock_get_model.return_value = juju.model.Model()
David Garcia12b29242020-09-17 16:01:48 +02002040 mock_consume.side_effect = juju.errors.JujuAPIError(
2041 {"error": "", "response": "", "request-id": ""}
2042 )
David Garcia68b00722020-09-11 15:05:00 +02002043
2044 with self.assertRaises(juju.errors.JujuAPIError):
2045 self.loop.run_until_complete(
David Garcia582b9232021-10-26 12:30:44 +02002046 self.libjuju.consume(
2047 "model_name", Offer(self.offer_url), self.provider_libjuju
2048 )
David Garcia68b00722020-09-11 15:05:00 +02002049 )
2050 mock_consume.assert_called_once()
2051 mock_disconnect_model.assert_called_once()
David Garcia582b9232021-10-26 12:30:44 +02002052 self.assertEqual(mock_disconnect_controller.call_count, 2)
David Garcia12b29242020-09-17 16:01:48 +02002053
2054
David Garcia475a7222020-09-21 16:19:15 +02002055@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_k8s_cloud_credential")
David Garcia12b29242020-09-17 16:01:48 +02002056@asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
2057class AddK8sTest(LibjujuTestCase):
2058 def setUp(self):
2059 super(AddK8sTest, self).setUp()
David Garciaf6e9b002020-11-27 15:32:02 +01002060 name = "cloud"
2061 rbac_id = generate_rbac_id()
2062 token = "token"
2063 client_cert_data = "cert"
2064 configuration = kubernetes.client.configuration.Configuration()
2065 storage_class = "storage_class"
2066 credential_name = name
2067
2068 self._add_k8s_args = {
2069 "name": name,
2070 "rbac_id": rbac_id,
2071 "token": token,
2072 "client_cert_data": client_cert_data,
2073 "configuration": configuration,
2074 "storage_class": storage_class,
2075 "credential_name": credential_name,
2076 }
David Garcia12b29242020-09-17 16:01:48 +02002077
David Garcia475a7222020-09-21 16:19:15 +02002078 def test_add_k8s(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garciaf6e9b002020-11-27 15:32:02 +01002079 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
David Garcia12b29242020-09-17 16:01:48 +02002080 mock_add_cloud.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02002081 mock_get_k8s_cloud_credential.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02002082
David Garcia475a7222020-09-21 16:19:15 +02002083 def test_add_k8s_exception(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garcia12b29242020-09-17 16:01:48 +02002084 mock_add_cloud.side_effect = Exception()
2085 with self.assertRaises(Exception):
David Garciaf6e9b002020-11-27 15:32:02 +01002086 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
David Garcia12b29242020-09-17 16:01:48 +02002087 mock_add_cloud.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02002088 mock_get_k8s_cloud_credential.assert_called_once()
David Garcia12b29242020-09-17 16:01:48 +02002089
David Garcia475a7222020-09-21 16:19:15 +02002090 def test_add_k8s_missing_name(self, mock_add_cloud, mock_get_k8s_cloud_credential):
David Garciaf6e9b002020-11-27 15:32:02 +01002091 self._add_k8s_args["name"] = ""
David Garcia12b29242020-09-17 16:01:48 +02002092 with self.assertRaises(Exception):
David Garciaf6e9b002020-11-27 15:32:02 +01002093 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
David Garcia12b29242020-09-17 16:01:48 +02002094 mock_add_cloud.assert_not_called()
2095
David Garcia475a7222020-09-21 16:19:15 +02002096 def test_add_k8s_missing_storage_name(
2097 self, mock_add_cloud, mock_get_k8s_cloud_credential
2098 ):
David Garciaf6e9b002020-11-27 15:32:02 +01002099 self._add_k8s_args["storage_class"] = ""
David Garcia12b29242020-09-17 16:01:48 +02002100 with self.assertRaises(Exception):
David Garciaf6e9b002020-11-27 15:32:02 +01002101 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
David Garcia12b29242020-09-17 16:01:48 +02002102 mock_add_cloud.assert_not_called()
2103
David Garcia475a7222020-09-21 16:19:15 +02002104 def test_add_k8s_missing_configuration_keys(
2105 self, mock_add_cloud, mock_get_k8s_cloud_credential
2106 ):
David Garciaf6e9b002020-11-27 15:32:02 +01002107 self._add_k8s_args["configuration"] = None
David Garcia12b29242020-09-17 16:01:48 +02002108 with self.assertRaises(Exception):
David Garciaf6e9b002020-11-27 15:32:02 +01002109 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
David Garcia12b29242020-09-17 16:01:48 +02002110 mock_add_cloud.assert_not_called()
2111
2112
2113@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2114@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2115@asynctest.mock.patch("juju.controller.Controller.add_cloud")
2116@asynctest.mock.patch("juju.controller.Controller.add_credential")
2117class AddCloudTest(LibjujuTestCase):
2118 def setUp(self):
2119 super(AddCloudTest, self).setUp()
2120 self.cloud = juju.client.client.Cloud()
2121 self.credential = juju.client.client.CloudCredential()
2122
2123 def test_add_cloud_with_credential(
2124 self,
2125 mock_add_credential,
2126 mock_add_cloud,
2127 mock_disconnect_controller,
2128 mock_get_controller,
2129 ):
2130 mock_get_controller.return_value = juju.controller.Controller()
2131
2132 cloud = self.loop.run_until_complete(
2133 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
2134 )
2135 self.assertEqual(cloud, self.cloud)
2136 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
2137 mock_add_credential.assert_called_once_with(
2138 "cloud", credential=self.credential, cloud="cloud"
2139 )
2140 mock_disconnect_controller.assert_called_once()
2141
2142 def test_add_cloud_no_credential(
2143 self,
2144 mock_add_credential,
2145 mock_add_cloud,
2146 mock_disconnect_controller,
2147 mock_get_controller,
2148 ):
2149 mock_get_controller.return_value = juju.controller.Controller()
2150
2151 cloud = self.loop.run_until_complete(
2152 self.libjuju.add_cloud("cloud", self.cloud)
2153 )
2154 self.assertEqual(cloud, self.cloud)
2155 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
2156 mock_add_credential.assert_not_called()
2157 mock_disconnect_controller.assert_called_once()
2158
2159 def test_add_cloud_exception(
2160 self,
2161 mock_add_credential,
2162 mock_add_cloud,
2163 mock_disconnect_controller,
2164 mock_get_controller,
2165 ):
2166 mock_get_controller.return_value = juju.controller.Controller()
2167 mock_add_cloud.side_effect = Exception()
2168 with self.assertRaises(Exception):
2169 self.loop.run_until_complete(
2170 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
2171 )
2172
2173 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
2174 mock_add_credential.assert_not_called()
2175 mock_disconnect_controller.assert_called_once()
2176
2177 def test_add_credential_exception(
2178 self,
2179 mock_add_credential,
2180 mock_add_cloud,
2181 mock_disconnect_controller,
2182 mock_get_controller,
2183 ):
2184 mock_get_controller.return_value = juju.controller.Controller()
2185 mock_add_credential.side_effect = Exception()
2186 with self.assertRaises(Exception):
2187 self.loop.run_until_complete(
2188 self.libjuju.add_cloud("cloud", self.cloud, credential=self.credential)
2189 )
2190
2191 mock_add_cloud.assert_called_once_with("cloud", self.cloud)
2192 mock_add_credential.assert_called_once_with(
2193 "cloud", credential=self.credential, cloud="cloud"
2194 )
2195 mock_disconnect_controller.assert_called_once()
2196
2197
2198@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2199@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2200@asynctest.mock.patch("juju.controller.Controller.remove_cloud")
2201class RemoveCloudTest(LibjujuTestCase):
2202 def setUp(self):
2203 super(RemoveCloudTest, self).setUp()
2204
2205 def test_remove_cloud(
David Garcia5b802c92020-11-11 16:56:06 +01002206 self,
2207 mock_remove_cloud,
2208 mock_disconnect_controller,
2209 mock_get_controller,
David Garcia12b29242020-09-17 16:01:48 +02002210 ):
2211 mock_get_controller.return_value = juju.controller.Controller()
2212
2213 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
2214 mock_remove_cloud.assert_called_once_with("cloud")
2215 mock_disconnect_controller.assert_called_once()
2216
2217 def test_remove_cloud_exception(
David Garcia5b802c92020-11-11 16:56:06 +01002218 self,
2219 mock_remove_cloud,
2220 mock_disconnect_controller,
2221 mock_get_controller,
David Garcia12b29242020-09-17 16:01:48 +02002222 ):
2223 mock_get_controller.return_value = juju.controller.Controller()
2224 mock_remove_cloud.side_effect = Exception()
2225
2226 with self.assertRaises(Exception):
2227 self.loop.run_until_complete(self.libjuju.remove_cloud("cloud"))
2228 mock_remove_cloud.assert_called_once_with("cloud")
2229 mock_disconnect_controller.assert_called_once()
David Garcia475a7222020-09-21 16:19:15 +02002230
2231
2232@asynctest.mock.patch("kubernetes.client.configuration.Configuration")
2233class GetK8sCloudCredentials(LibjujuTestCase):
2234 def setUp(self):
2235 super(GetK8sCloudCredentials, self).setUp()
David Garciaf6e9b002020-11-27 15:32:02 +01002236 self.cert_data = "cert"
2237 self.token = "token"
David Garcia475a7222020-09-21 16:19:15 +02002238
2239 @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
2240 def test_not_supported(self, mock_exception, mock_configuration):
2241 mock_configuration.username = ""
2242 mock_configuration.password = ""
2243 mock_configuration.ssl_ca_cert = None
2244 mock_configuration.cert_file = None
2245 mock_configuration.key_file = None
2246 exception_raised = False
David Garciaf6e9b002020-11-27 15:32:02 +01002247 self.token = None
2248 self.cert_data = None
David Garcia475a7222020-09-21 16:19:15 +02002249 try:
David Garciaf6e9b002020-11-27 15:32:02 +01002250 _ = self.libjuju.get_k8s_cloud_credential(
2251 mock_configuration,
2252 self.cert_data,
2253 self.token,
2254 )
David Garcia475a7222020-09-21 16:19:15 +02002255 except JujuInvalidK8sConfiguration as e:
2256 exception_raised = True
2257 self.assertEqual(
David Garcia5b802c92020-11-11 16:56:06 +01002258 e.message,
2259 "authentication method not supported",
David Garcia475a7222020-09-21 16:19:15 +02002260 )
2261 self.assertTrue(exception_raised)
2262
2263 def test_user_pass(self, mock_configuration):
2264 mock_configuration.username = "admin"
2265 mock_configuration.password = "admin"
2266 mock_configuration.ssl_ca_cert = None
2267 mock_configuration.cert_file = None
2268 mock_configuration.key_file = None
David Garciaf6e9b002020-11-27 15:32:02 +01002269 self.token = None
2270 self.cert_data = None
2271 credential = self.libjuju.get_k8s_cloud_credential(
2272 mock_configuration,
2273 self.cert_data,
2274 self.token,
2275 )
David Garcia475a7222020-09-21 16:19:15 +02002276 self.assertEqual(
2277 credential,
2278 juju.client._definitions.CloudCredential(
2279 attrs={"username": "admin", "password": "admin"}, auth_type="userpass"
2280 ),
2281 )
2282
David Garciaf6e9b002020-11-27 15:32:02 +01002283 def test_user_pass_with_cert(self, mock_configuration):
2284 mock_configuration.username = "admin"
2285 mock_configuration.password = "admin"
2286 mock_configuration.ssl_ca_cert = None
2287 mock_configuration.cert_file = None
2288 mock_configuration.key_file = None
2289 self.token = None
2290 credential = self.libjuju.get_k8s_cloud_credential(
2291 mock_configuration,
2292 self.cert_data,
2293 self.token,
2294 )
2295 self.assertEqual(
2296 credential,
2297 juju.client._definitions.CloudCredential(
2298 attrs={
2299 "ClientCertificateData": self.cert_data,
2300 "username": "admin",
2301 "password": "admin",
2302 },
2303 auth_type="userpasswithcert",
2304 ),
2305 )
2306
David Garcia475a7222020-09-21 16:19:15 +02002307 def test_user_no_pass(self, mock_configuration):
2308 mock_configuration.username = "admin"
2309 mock_configuration.password = ""
2310 mock_configuration.ssl_ca_cert = None
2311 mock_configuration.cert_file = None
2312 mock_configuration.key_file = None
David Garciaf6e9b002020-11-27 15:32:02 +01002313 self.token = None
2314 self.cert_data = None
David Garciaeb8943a2021-04-12 12:07:37 +02002315 with patch.object(self.libjuju.log, "debug") as mock_debug:
David Garciaf6e9b002020-11-27 15:32:02 +01002316 credential = self.libjuju.get_k8s_cloud_credential(
2317 mock_configuration,
2318 self.cert_data,
2319 self.token,
2320 )
David Garcia475a7222020-09-21 16:19:15 +02002321 self.assertEqual(
2322 credential,
2323 juju.client._definitions.CloudCredential(
2324 attrs={"username": "admin", "password": ""}, auth_type="userpass"
2325 ),
2326 )
2327 mock_debug.assert_called_once_with(
2328 "credential for user admin has empty password"
2329 )
2330
David Garcia475a7222020-09-21 16:19:15 +02002331 def test_cert(self, mock_configuration):
2332 mock_configuration.username = ""
2333 mock_configuration.password = ""
2334 mock_configuration.api_key = {"authorization": "Bearer Token"}
2335 ssl_ca_cert = tempfile.NamedTemporaryFile()
2336 with open(ssl_ca_cert.name, "w") as ssl_ca_cert_file:
2337 ssl_ca_cert_file.write("cacert")
2338 mock_configuration.ssl_ca_cert = ssl_ca_cert.name
2339 mock_configuration.cert_file = None
2340 mock_configuration.key_file = None
David Garciaf6e9b002020-11-27 15:32:02 +01002341 credential = self.libjuju.get_k8s_cloud_credential(
2342 mock_configuration,
2343 self.cert_data,
2344 self.token,
2345 )
David Garcia475a7222020-09-21 16:19:15 +02002346 self.assertEqual(
2347 credential,
2348 juju.client._definitions.CloudCredential(
David Garciaf6e9b002020-11-27 15:32:02 +01002349 attrs={"ClientCertificateData": self.cert_data, "Token": self.token},
David Garcia475a7222020-09-21 16:19:15 +02002350 auth_type="certificate",
2351 ),
2352 )
2353
David Garciaf6e9b002020-11-27 15:32:02 +01002354 # TODO: Fix this test when oauth authentication is supported
2355 # def test_oauth2(self, mock_configuration):
2356 # mock_configuration.username = ""
2357 # mock_configuration.password = ""
2358 # mock_configuration.api_key = {"authorization": "Bearer Token"}
2359 # key = tempfile.NamedTemporaryFile()
2360 # with open(key.name, "w") as key_file:
2361 # key_file.write("key")
2362 # mock_configuration.ssl_ca_cert = None
2363 # mock_configuration.cert_file = None
2364 # mock_configuration.key_file = key.name
2365 # credential = self.libjuju.get_k8s_cloud_credential(
2366 # mock_configuration,
2367 # self.cert_data,
2368 # self.token,
2369 # )
2370 # self.assertEqual(
2371 # credential,
2372 # juju.client._definitions.CloudCredential(
2373 # attrs={"ClientKeyData": "key", "Token": "Token"},
2374 # auth_type="oauth2",
2375 # ),
2376 # )
David Garcia475a7222020-09-21 16:19:15 +02002377
David Garciaf6e9b002020-11-27 15:32:02 +01002378 # @asynctest.mock.patch("n2vc.exceptions.JujuInvalidK8sConfiguration")
2379 # def test_oauth2_missing_token(self, mock_exception, mock_configuration):
2380 # mock_configuration.username = ""
2381 # mock_configuration.password = ""
2382 # key = tempfile.NamedTemporaryFile()
2383 # with open(key.name, "w") as key_file:
2384 # key_file.write("key")
2385 # mock_configuration.ssl_ca_cert = None
2386 # mock_configuration.cert_file = None
2387 # mock_configuration.key_file = key.name
2388 # exception_raised = False
2389 # try:
2390 # _ = self.libjuju.get_k8s_cloud_credential(
2391 # mock_configuration,
2392 # self.cert_data,
2393 # self.token,
2394 # )
2395 # except JujuInvalidK8sConfiguration as e:
2396 # exception_raised = True
2397 # self.assertEqual(
2398 # e.message,
2399 # "missing token for auth type oauth2",
2400 # )
2401 # self.assertTrue(exception_raised)
David Garcia475a7222020-09-21 16:19:15 +02002402
2403 def test_exception_cannot_set_token_and_userpass(self, mock_configuration):
2404 mock_configuration.username = "admin"
2405 mock_configuration.password = "pass"
2406 mock_configuration.api_key = {"authorization": "No_bearer_token"}
2407 mock_configuration.ssl_ca_cert = None
2408 mock_configuration.cert_file = None
2409 mock_configuration.key_file = None
2410 exception_raised = False
2411 try:
David Garciaf6e9b002020-11-27 15:32:02 +01002412 _ = self.libjuju.get_k8s_cloud_credential(
2413 mock_configuration,
2414 self.cert_data,
2415 self.token,
2416 )
David Garcia475a7222020-09-21 16:19:15 +02002417 except JujuInvalidK8sConfiguration as e:
2418 exception_raised = True
2419 self.assertEqual(
David Garcia5b802c92020-11-11 16:56:06 +01002420 e.message,
2421 "Cannot set both token and user/pass",
David Garcia475a7222020-09-21 16:19:15 +02002422 )
2423 self.assertTrue(exception_raised)
aktas2962f3e2021-03-15 11:05:35 +03002424
2425
2426@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2427@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2428@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2429@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2430@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2431@asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for_model")
2432class ScaleApplicationTest(LibjujuTestCase):
2433 def setUp(self):
2434 super(ScaleApplicationTest, self).setUp()
2435
2436 @asynctest.mock.patch("asyncio.sleep")
2437 def test_scale_application(
2438 self,
2439 mock_sleep,
2440 mock_wait_for_model,
2441 mock_disconnect_controller,
2442 mock_disconnect_model,
2443 mock_get_application,
2444 mock_get_model,
2445 mock_get_controller,
2446 ):
2447 mock_get_model.return_value = juju.model.Model()
2448 mock_get_application.return_value = FakeApplication()
garciadeblas82b591c2021-03-24 09:22:13 +01002449 self.loop.run_until_complete(self.libjuju.scale_application("model", "app", 2))
aktas2962f3e2021-03-15 11:05:35 +03002450 mock_wait_for_model.assert_called_once()
2451 mock_disconnect_controller.assert_called_once()
2452 mock_disconnect_model.assert_called_once()
2453
2454 def test_no_application(
2455 self,
2456 mock_wait_for,
2457 mock_disconnect_controller,
2458 mock_disconnect_model,
2459 mock_get_application,
2460 mock_get_model,
2461 mock_get_controller,
2462 ):
2463 mock_get_application.return_value = None
2464 mock_get_model.return_value = juju.model.Model()
2465 with self.assertRaises(JujuApplicationNotFound):
2466 self.loop.run_until_complete(
garciadeblas82b591c2021-03-24 09:22:13 +01002467 self.libjuju.scale_application("model", "app", 2)
aktas2962f3e2021-03-15 11:05:35 +03002468 )
2469 mock_disconnect_controller.assert_called()
2470 mock_disconnect_model.assert_called()
2471
2472 def test_exception(
garciadeblas82b591c2021-03-24 09:22:13 +01002473 self,
2474 mock_wait_for,
2475 mock_disconnect_controller,
2476 mock_disconnect_model,
2477 mock_get_application,
2478 mock_get_model,
2479 mock_get_controller,
aktas2962f3e2021-03-15 11:05:35 +03002480 ):
2481 mock_get_model.return_value = None
2482 mock_get_application.return_value = FakeApplication()
2483 with self.assertRaises(Exception):
2484 self.loop.run_until_complete(
garciadeblas82b591c2021-03-24 09:22:13 +01002485 self.libjuju.scale_application("model", "app", 2, total_timeout=0)
aktas2962f3e2021-03-15 11:05:35 +03002486 )
2487 mock_disconnect_controller.assert_called_once()
2488
2489
2490@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2491class GetUnitNumberTest(LibjujuTestCase):
2492 def setUp(self):
2493 super(GetUnitNumberTest, self).setUp()
2494
aktasfa02f8a2021-07-29 17:41:40 +03002495 def test_successful_get_unit_number(
aktas2962f3e2021-03-15 11:05:35 +03002496 self,
2497 mock_get_applications,
2498 ):
2499 mock_get_applications.return_value = FakeApplication()
2500 model = juju.model.Model()
2501 result = self.libjuju._get_application_count(model, "app")
2502 self.assertEqual(result, 2)
2503
2504 def test_non_existing_application(
2505 self,
2506 mock_get_applications,
2507 ):
2508 mock_get_applications.return_value = None
2509 model = juju.model.Model()
2510 result = self.libjuju._get_application_count(model, "app")
2511 self.assertEqual(result, None)
aktasfa02f8a2021-07-29 17:41:40 +03002512
2513
2514@asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
2515class GetMachineInfoTest(LibjujuTestCase):
2516 def setUp(self):
2517 super(GetMachineInfoTest, self).setUp()
2518
2519 def test_successful(
2520 self,
2521 mock_machines,
2522 ):
2523 machine_id = "existing_machine"
2524 model = juju.model.Model()
2525 mock_machines.return_value = {"existing_machine": FakeManualMachine()}
2526 machine, series = self.libjuju._get_machine_info(
2527 machine_id=machine_id,
2528 model=model,
2529 )
2530 self.assertIsNotNone(machine, series)
2531
2532 def test_exception(
2533 self,
2534 mock_machines,
2535 ):
2536 machine_id = "not_existing_machine"
2537 machine = series = None
2538 model = juju.model.Model()
2539 mock_machines.return_value = {"existing_machine": FakeManualMachine()}
2540 with self.assertRaises(JujuMachineNotFound):
2541 machine, series = self.libjuju._get_machine_info(
2542 machine_id=machine_id,
2543 model=model,
2544 )
2545 self.assertIsNone(machine, series)
2546
2547
2548class GetUnitTest(LibjujuTestCase):
2549 def setUp(self):
2550 super(GetUnitTest, self).setUp()
2551
2552 def test_successful(self):
2553 result = self.libjuju._get_unit(FakeApplication(), "existing_machine_id")
2554 self.assertIsInstance(result, FakeUnit)
2555
2556 def test_return_none(self):
2557 result = self.libjuju._get_unit(FakeApplication(), "not_existing_machine_id")
2558 self.assertIsNone(result)
2559
2560
2561@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2562@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2563@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2564@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2565@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2566class CheckApplicationExists(LibjujuTestCase):
2567 def setUp(self):
2568 super(CheckApplicationExists, self).setUp()
2569
2570 def test_successful(
2571 self,
2572 mock_get_application,
2573 mock_disconnect_controller,
2574 mock_disconnect_model,
2575 mock_get_model,
2576 mock_get_controller,
2577 ):
2578 mock_get_model.return_value = juju.model.Model()
2579 mock_get_application.return_value = FakeApplication()
2580 result = self.loop.run_until_complete(
2581 self.libjuju.check_application_exists(
2582 "model",
2583 "app",
2584 )
2585 )
2586 self.assertEqual(result, True)
2587
2588 mock_get_application.assert_called_once()
2589 mock_get_controller.assert_called_once()
2590 mock_get_model.assert_called_once()
2591 mock_disconnect_controller.assert_called_once()
2592 mock_disconnect_model.assert_called_once()
2593
2594 def test_no_application(
2595 self,
2596 mock_get_application,
2597 mock_disconnect_controller,
2598 mock_disconnect_model,
2599 mock_get_model,
2600 mock_get_controller,
2601 ):
2602 mock_get_model.return_value = juju.model.Model()
2603 mock_get_application.return_value = None
2604 result = self.loop.run_until_complete(
2605 self.libjuju.check_application_exists(
2606 "model",
2607 "app",
2608 )
2609 )
2610 self.assertEqual(result, False)
2611
2612 mock_get_application.assert_called_once()
2613 mock_get_controller.assert_called_once()
2614 mock_get_model.assert_called_once()
2615 mock_disconnect_controller.assert_called_once()
2616 mock_disconnect_model.assert_called_once()
2617
2618
2619@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2620@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2621@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2622@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2623@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2624@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_machine_info")
2625class AddUnitTest(LibjujuTestCase):
2626 def setUp(self):
2627 super(AddUnitTest, self).setUp()
2628
2629 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for")
2630 @asynctest.mock.patch("asyncio.sleep")
2631 def test_successful(
2632 self,
2633 mock_sleep,
2634 mock_wait_for,
2635 mock_get_machine_info,
2636 mock_get_application,
2637 mock_disconnect_controller,
2638 mock_disconnect_model,
2639 mock_get_model,
2640 mock_get_controller,
2641 ):
2642 mock_get_model.return_value = juju.model.Model()
2643 mock_get_application.return_value = FakeApplication()
2644 mock_get_machine_info.return_value = FakeMachine(), "series"
2645 self.loop.run_until_complete(
2646 self.libjuju.add_unit(
2647 "existing_app",
2648 "model",
2649 "machine",
2650 )
2651 )
2652
2653 mock_wait_for.assert_called_once()
2654 mock_get_application.assert_called_once()
2655 mock_get_controller.assert_called_once()
2656 mock_get_model.assert_called_once()
2657 mock_disconnect_controller.assert_called_once()
2658 mock_disconnect_model.assert_called_once()
2659
2660 def test_no_app(
2661 self,
2662 mock_get_machine_info,
2663 mock_get_application,
2664 mock_disconnect_controller,
2665 mock_disconnect_model,
2666 mock_get_model,
2667 mock_get_controller,
2668 ):
2669 mock_get_model.return_value = juju.model.Model()
2670 mock_get_application.return_value = None
2671 with self.assertRaises(JujuApplicationNotFound):
2672 self.loop.run_until_complete(
2673 self.libjuju.add_unit(
2674 "existing_app",
2675 "model",
2676 "machine",
2677 )
2678 )
2679
2680 mock_get_application.assert_called_once()
2681 mock_get_controller.assert_called_once()
2682 mock_get_model.assert_called_once()
2683 mock_disconnect_controller.assert_called_once()
2684 mock_disconnect_model.assert_called_once()
2685
2686 def test_no_machine(
2687 self,
2688 mock_get_machine_info,
2689 mock_get_application,
2690 mock_disconnect_controller,
2691 mock_disconnect_model,
2692 mock_get_model,
2693 mock_get_controller,
2694 ):
2695 mock_get_model.return_value = juju.model.Model()
2696 mock_get_application.return_value = FakeApplication()
2697 mock_get_machine_info.side_effect = JujuMachineNotFound()
2698 with self.assertRaises(JujuMachineNotFound):
2699 self.loop.run_until_complete(
2700 self.libjuju.add_unit(
2701 "existing_app",
2702 "model",
2703 "machine",
2704 )
2705 )
2706
2707 mock_get_application.assert_called_once()
2708 mock_get_controller.assert_called_once()
2709 mock_get_model.assert_called_once()
2710 mock_disconnect_controller.assert_called_once()
2711 mock_disconnect_model.assert_called_once()
2712
2713
2714@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
2715@asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
2716@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
2717@asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
2718@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2719@asynctest.mock.patch("n2vc.libjuju.Libjuju._get_unit")
2720class DestroyUnitTest(LibjujuTestCase):
2721 def setUp(self):
2722 super(DestroyUnitTest, self).setUp()
2723
2724 @asynctest.mock.patch("asyncio.sleep")
2725 def test_successful(
2726 self,
2727 mock_sleep,
2728 mock_get_unit,
2729 mock_get_application,
2730 mock_disconnect_controller,
2731 mock_disconnect_model,
2732 mock_get_model,
2733 mock_get_controller,
2734 ):
2735 mock_get_model.return_value = juju.model.Model()
2736 mock_get_application.return_value = FakeApplication()
2737
2738 self.loop.run_until_complete(
2739 self.libjuju.destroy_unit("app", "model", "machine", 0)
2740 )
2741
2742 mock_get_unit.assert_called()
2743 mock_get_application.assert_called_once()
2744 mock_get_controller.assert_called_once()
2745 mock_get_model.assert_called_once()
2746 mock_disconnect_controller.assert_called_once()
2747 mock_disconnect_model.assert_called_once()
2748
2749 def test_no_app(
2750 self,
2751 mock_get_unit,
2752 mock_get_application,
2753 mock_disconnect_controller,
2754 mock_disconnect_model,
2755 mock_get_model,
2756 mock_get_controller,
2757 ):
2758 mock_get_model.return_value = juju.model.Model()
2759 mock_get_application.return_value = None
2760
2761 with self.assertRaises(JujuApplicationNotFound):
2762 self.loop.run_until_complete(
2763 self.libjuju.destroy_unit("app", "model", "machine")
2764 )
2765
2766 mock_get_application.assert_called_once()
2767 mock_get_controller.assert_called_once()
2768 mock_get_model.assert_called_once()
2769 mock_disconnect_controller.assert_called_once()
2770 mock_disconnect_model.assert_called_once()
2771
2772 def test_no_unit(
2773 self,
2774 mock_get_unit,
2775 mock_get_application,
2776 mock_disconnect_controller,
2777 mock_disconnect_model,
2778 mock_get_model,
2779 mock_get_controller,
2780 ):
2781 mock_get_model.return_value = juju.model.Model()
2782 mock_get_application.return_value = FakeApplication()
2783 mock_get_unit.return_value = None
2784
2785 with self.assertRaises(JujuError):
2786 self.loop.run_until_complete(
2787 self.libjuju.destroy_unit("app", "model", "machine")
2788 )
2789
2790 mock_get_unit.assert_called_once()
2791 mock_get_application.assert_called_once()
2792 mock_get_controller.assert_called_once()
2793 mock_get_model.assert_called_once()
2794 mock_disconnect_controller.assert_called_once()
2795 mock_disconnect_model.assert_called_once()