38d8d0e48320aa6a66243b771258486022f9fbf9
[osm/N2VC.git] / n2vc / tests / unit / test_libjuju.py
1 # Copyright 2020 Canonical Ltd.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import asyncio
16 import asynctest
17 import tempfile
18 from unittest.mock import Mock, patch
19 import juju
20 import kubernetes
21 from juju.errors import JujuAPIError
22 import logging
23
24 from n2vc.definitions import Offer, RelationEndpoint
25 from .utils import (
26 FakeApplication,
27 FakeMachine,
28 FakeManualMachine,
29 FakeUnit,
30 )
31 from n2vc.libjuju import Libjuju
32 from n2vc.exceptions import (
33 JujuControllerFailedConnecting,
34 JujuMachineNotFound,
35 JujuApplicationNotFound,
36 JujuActionNotFound,
37 JujuApplicationExists,
38 JujuInvalidK8sConfiguration,
39 JujuLeaderUnitNotFound,
40 JujuError,
41 )
42 from n2vc.k8s_juju_conn import generate_rbac_id
43 from n2vc.tests.unit.utils import AsyncMock
44 from n2vc.vca.connection import Connection
45 from n2vc.vca.connection_data import ConnectionData
46
47
48 cacert = """-----BEGIN CERTIFICATE-----
49 SOMECERT
50 -----END CERTIFICATE-----"""
51
52
53 @asynctest.mock.patch("n2vc.libjuju.Controller")
54 class LibjujuTestCase(asynctest.TestCase):
55 @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
56 def setUp(
57 self,
58 mock_base64_to_cacert=None,
59 ):
60 self.loop = asyncio.get_event_loop()
61 self.db = Mock()
62 mock_base64_to_cacert.return_value = cacert
63 # Connection._load_vca_connection_data = Mock()
64 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 }
79 )
80 logging.disable(logging.CRITICAL)
81 self.libjuju = Libjuju(vca_connection)
82 self.loop.run_until_complete(self.libjuju.disconnect())
83
84
85 @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 )
90 class GetControllerTest(LibjujuTestCase):
91 def setUp(self):
92 super(GetControllerTest, self).setUp()
93
94 def test_diff_endpoint(self, mock_api_endpoints, mock_connect):
95 self.libjuju.endpoints = []
96 controller = self.loop.run_until_complete(self.libjuju.get_controller())
97 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,
103 mock_api_endpoints,
104 mock_connect,
105 ):
106 self.libjuju.endpoints = []
107
108 mock_connect.side_effect = Exception()
109 controller = None
110 with self.assertRaises(JujuControllerFailedConnecting):
111 controller = self.loop.run_until_complete(self.libjuju.get_controller())
112 self.assertIsNone(controller)
113 mock_disconnect_controller.assert_called()
114
115 def test_same_endpoint_get_controller(self, mock_api_endpoints, mock_connect):
116 self.libjuju.endpoints = ["127.0.0.1:17070"]
117 controller = self.loop.run_until_complete(self.libjuju.get_controller())
118 self.assertIsInstance(controller, juju.controller.Controller)
119
120
121 class 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")
143 class 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
157 # This should not raise an exception
158 self.loop.run_until_complete(self.libjuju.add_model("existing_model", "cloud"))
159
160 mock_disconnect_controller.assert_called()
161
162 # TODO Check two job executing at the same time and one returning without doing anything.
163
164 def test_non_existing_model(
165 self,
166 mock_disconnect_model,
167 mock_disconnect_controller,
168 mock_add_model,
169 mock_model_exists,
170 mock_get_controller,
171 ):
172 mock_model_exists.return_value = False
173 mock_get_controller.return_value = juju.controller.Controller()
174
175 self.loop.run_until_complete(
176 self.libjuju.add_model("nonexisting_model", Mock())
177 )
178
179 mock_add_model.assert_called_once()
180 mock_disconnect_controller.assert_called()
181 mock_disconnect_model.assert_called()
182
183
184 @asynctest.mock.patch("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")
194 class 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 )
238 expected_result = [
239 {
240 "id": "id",
241 "action": "action_name",
242 "status": "status",
243 "output": "completed",
244 }
245 ]
246 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")
260 class 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(
275 self.libjuju.get_application_configs("model", "app")
276 )
277
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()
292 application_configs = self.loop.run_until_complete(
293 self.libjuju.get_application_configs("model", "app")
294 )
295
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
304 @asynctest.mock.patch("juju.controller.Controller.get_model")
305 class GetModelTest(LibjujuTestCase):
306 def setUp(self):
307 super(GetModelTest, self).setUp()
308
309 def test_get_model(
310 self,
311 mock_get_model,
312 ):
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")
322 class ModelExistsTest(LibjujuTestCase):
323 def setUp(self):
324 super(ModelExistsTest, self).setUp()
325
326 async def test_existing_model(
327 self,
328 mock_list_models,
329 mock_get_controller,
330 ):
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(
340 self,
341 mock_disconnect_controller,
342 mock_list_models,
343 mock_get_controller,
344 ):
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(
351 self,
352 mock_list_models,
353 mock_get_controller,
354 ):
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")
368 class 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
391 def test_exception(
392 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()
401 status = None
402 with self.assertRaises(Exception):
403 status = self.loop.run_until_complete(
404 self.libjuju.get_model_status("model")
405 )
406
407 mock_disconnect_controller.assert_called_once()
408 mock_disconnect_model.assert_called_once()
409
410 self.assertIsNone(status)
411
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")
420 class 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 ):
456 machine = None
457 bool_res = None
458 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 )
463 self.assertIsNone(machine)
464 self.assertIsNone(bool_res)
465
466 mock_disconnect_controller.assert_called()
467 mock_disconnect_model.assert_called()
468
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
499 @asynctest.mock.patch("os.remove")
500 @asynctest.mock.patch("n2vc.libjuju.yaml.dump")
501 @asynctest.mock.patch("builtins.open", create=True)
502 @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")
506 @asynctest.mock.patch("n2vc.juju_watcher.JujuModelWatcher.wait_for_model")
507 @asynctest.mock.patch("juju.model.Model.deploy")
508 @asynctest.mock.patch("juju.model.CharmhubDeployType.resolve")
509 @asynctest.mock.patch("n2vc.libjuju.BundleHandler")
510 @asynctest.mock.patch("juju.url.URL.parse")
511 class DeployTest(LibjujuTestCase):
512 def setUp(self):
513 super(DeployTest, self).setUp()
514 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()
557
558 def test_deploy(
559 self,
560 mock_url_parse,
561 mock_bundle,
562 mock_resolve,
563 mock_deploy,
564 mock_wait_for_model,
565 mock_disconnect_controller,
566 mock_disconnect_model,
567 mock_get_model,
568 mock_get_controller,
569 mocked_file,
570 mock_yaml,
571 mock_os,
572 ):
573 self.setup_bundle_download_mocks(
574 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
575 )
576 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=[])
590 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,
596 mock_url_parse,
597 mock_bundle,
598 mock_resolve,
599 mock_deploy,
600 mock_wait_for_model,
601 mock_disconnect_controller,
602 mock_disconnect_model,
603 mock_get_model,
604 mock_get_controller,
605 mocked_file,
606 mock_yaml,
607 mock_os,
608 ):
609 self.setup_bundle_download_mocks(
610 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
611 )
612 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=[])
620 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,
626 mock_url_parse,
627 mock_bundle,
628 mock_resolve,
629 mock_deploy,
630 mock_wait_for_model,
631 mock_disconnect_controller,
632 mock_disconnect_model,
633 mock_get_model,
634 mock_get_controller,
635 mocked_file,
636 mock_yaml,
637 mock_os,
638 ):
639 self.setup_bundle_download_mocks(
640 mock_url_parse, mock_bundle, mock_resolve, mock_get_model
641 )
642 mock_deploy.side_effect = Exception()
643 with self.assertRaises(Exception):
644 self.loop.run_until_complete(self.libjuju.deploy("cs:osm", "model"))
645 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)
647 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
652 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
901
902 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_controller")
903 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_model")
904 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_model")
905 @asynctest.mock.patch("n2vc.libjuju.Libjuju.disconnect_controller")
906 @asynctest.mock.patch(
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")
913 class 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
932 application = None
933 with self.assertRaises(JujuApplicationExists):
934 application = self.loop.run_until_complete(
935 self.libjuju.deploy_charm(
936 "existing_app",
937 "path",
938 "model",
939 "machine",
940 )
941 )
942 self.assertIsNone(application)
943
944 mock_disconnect_controller.assert_called()
945 mock_disconnect_model.assert_called()
946
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()}
961 application = None
962 with self.assertRaises(JujuMachineNotFound):
963 application = self.loop.run_until_complete(
964 self.libjuju.deploy_charm(
965 "app",
966 "path",
967 "model",
968 "machine",
969 )
970 )
971
972 self.assertIsNone(application)
973
974 mock_disconnect_controller.assert_called()
975 mock_disconnect_model.assert_called()
976
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(
995 "app",
996 "path",
997 "model",
998 "existing_machine",
999 num_units=2,
1000 )
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 )
1044 class GetApplicationTest(LibjujuTestCase):
1045 def setUp(self):
1046 super(GetApplicationTest, self).setUp()
1047
1048 def test_existing_application(
1049 self,
1050 mock_applications,
1051 ):
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(
1058 self,
1059 mock_applications,
1060 ):
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")
1075 class 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()
1092 output = None
1093 status = None
1094 with self.assertRaises(JujuApplicationNotFound):
1095 output, status = self.loop.run_until_complete(
1096 self.libjuju.execute_action(
1097 "app",
1098 "model",
1099 "action",
1100 )
1101 )
1102 self.assertIsNone(output)
1103 self.assertIsNone(status)
1104
1105 mock_disconnect_controller.assert_called()
1106 mock_disconnect_model.assert_called()
1107
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 ):
1119 mock_get_model.return_value = juju.model.Model()
1120 mock__get_application.return_value = FakeApplication()
1121 output = None
1122 status = None
1123 with self.assertRaises(JujuActionNotFound):
1124 output, status = self.loop.run_until_complete(
1125 self.libjuju.execute_action(
1126 "app",
1127 "model",
1128 "action",
1129 )
1130 )
1131 self.assertIsNone(output)
1132 self.assertIsNone(status)
1133
1134 mock_disconnect_controller.assert_called()
1135 mock_disconnect_model.assert_called()
1136
1137 @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(
1159 self.libjuju.execute_action(
1160 "app",
1161 "model",
1162 "action",
1163 )
1164 )
1165 self.assertIsNone(output)
1166 self.assertIsNone(status)
1167
1168 mock_disconnect_controller.assert_called()
1169 mock_disconnect_model.assert_called()
1170
1171 def test_successful_exec(
1172 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")
1203 class 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()
1216 actions = None
1217 with self.assertRaises(Exception):
1218 actions = self.loop.run_until_complete(
1219 self.libjuju.get_actions("app", "model")
1220 )
1221
1222 self.assertIsNone(actions)
1223 mock_disconnect_controller.assert_called_once()
1224 mock_disconnect_model.assert_called_once()
1225
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")
1250 @asynctest.mock.patch("juju.application.Application.get_metrics")
1251 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
1252 class 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")
1314 @asynctest.mock.patch("juju.model.Model.add_relation")
1315 class 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(
1336 self.libjuju.add_relation(
1337 "model",
1338 "app1:relation1",
1339 "app2:relation2",
1340 )
1341 )
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")
1348 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")
1380 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(
1396 self.libjuju.add_relation(
1397 "model",
1398 "app1:relation1",
1399 "app2:relation2",
1400 )
1401 )
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
1407 @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
1441 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(
1455 self.libjuju.add_relation(
1456 "model",
1457 "app1:relation1",
1458 "app2:relation2",
1459 )
1460 )
1461
1462 mock_disconnect_controller.assert_called_once()
1463 mock_disconnect_model.assert_called_once()
1464
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(
1476 self.libjuju.add_relation(
1477 "model",
1478 "app1:relation1",
1479 "app2:relation2",
1480 )
1481 )
1482
1483 mock_add_relation.assert_called_with("app1:relation1", "app2:relation2")
1484 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(
1498 self.libjuju.add_relation(
1499 "model",
1500 "app1:relation1",
1501 "saas_name",
1502 )
1503 )
1504
1505 mock_add_relation.assert_called_with("app1:relation1", "saas_name")
1506 mock_disconnect_controller.assert_called_once()
1507 mock_disconnect_model.assert_called_once()
1508
1509
1510 # TODO destroy_model testcase
1511
1512
1513 @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")
1518 class DestroyApplicationTest(LibjujuTestCase):
1519 def setUp(self):
1520 super(DestroyApplicationTest, self).setUp()
1521
1522 def test_success(
1523 self,
1524 mock_get_controller,
1525 mock_get_model,
1526 mock_disconnect_controller,
1527 mock_get_application,
1528 mock_disconnect_model,
1529 ):
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(
1544 self,
1545 mock_get_controller,
1546 mock_get_model,
1547 mock_disconnect_controller,
1548 mock_get_application,
1549 mock_disconnect_model,
1550 ):
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(
1564 self,
1565 mock_get_controller,
1566 mock_get_model,
1567 mock_disconnect_controller,
1568 mock_get_application,
1569 mock_disconnect_model,
1570 ):
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
1585 # @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()
1590
1591 # 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)
1607
1608 # 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")
1616
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")
1623 class 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 ):
1635 mock_get_application.return_value = FakeApplication()
1636
1637 self.loop.run_until_complete(
1638 self.libjuju.configure_application(
1639 "model",
1640 "app",
1641 {"config"},
1642 )
1643 )
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 ):
1656 mock_get_application.side_effect = Exception()
1657
1658 with self.assertRaises(Exception):
1659 self.loop.run_until_complete(
1660 self.libjuju.configure_application(
1661 "model",
1662 "app",
1663 {"config"},
1664 )
1665 )
1666 mock_disconnect_controller.assert_called_once()
1667 mock_disconnect_model.assert_called_once()
1668
1669 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 ):
1677 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 ):
1701 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
1716
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")
1725 class ListModelsTest(LibjujuTestCase):
1726 def setUp(self):
1727 super(ListModelsTest, self).setUp()
1728
1729 def test_containing(
1730 self,
1731 mock_list_models,
1732 mock_disconnect_controller,
1733 mock_get_controller,
1734 ):
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(
1743 self,
1744 mock_list_models,
1745 mock_disconnect_controller,
1746 mock_get_controller,
1747 ):
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(
1756 self,
1757 mock_list_models,
1758 mock_disconnect_controller,
1759 mock_get_controller,
1760 ):
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"])
1767
1768
1769 @asynctest.mock.patch("n2vc.libjuju.Libjuju.list_models")
1770 class 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, [])
1801
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")
1806 class ListOffers(LibjujuTestCase):
1807 def setUp(self):
1808 super(ListOffers, self).setUp()
1809
1810 def test_disconnect_controller(
1811 self,
1812 mock_list_offers,
1813 mock_disconnect_controller,
1814 mock_get_controller,
1815 ):
1816 mock_get_controller.return_value = juju.controller.Controller()
1817 mock_list_offers.side_effect = Exception()
1818 with self.assertRaises(Exception):
1819 self.loop.run_until_complete(self.libjuju._list_offers("model"))
1820 mock_disconnect_controller.assert_called_once()
1821
1822 def test_empty_list(
1823 self,
1824 mock_list_offers,
1825 mock_disconnect_controller,
1826 mock_get_controller,
1827 ):
1828 mock_get_controller.return_value = juju.controller.Controller()
1829 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"))
1833 self.assertEqual(offers, [])
1834 mock_disconnect_controller.assert_called_once()
1835
1836 def test_non_empty_list(
1837 self,
1838 mock_list_offers,
1839 mock_disconnect_controller,
1840 mock_get_controller,
1841 ):
1842 mock_get_controller.return_value = juju.controller.Controller()
1843 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])
1849 mock_disconnect_controller.assert_called_once()
1850
1851 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")
1898 class 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
1946
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")
1952 class ConsumeTest(LibjujuTestCase):
1953 def setUp(self):
1954 self.offer_url = "admin/model.offer_name"
1955 super(ConsumeTest, self).setUp()
1956 self.provider_libjuju = self.libjuju
1957
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 ):
1966 self_controller = juju.controller.Controller()
1967 provider_controller = juju.controller.Controller()
1968 mock_get_controller.side_effect = [self_controller, provider_controller]
1969 mock_get_model.return_value = juju.model.Model()
1970
1971 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 )
1983 mock_disconnect_model.assert_called_once()
1984 self.assertEqual(mock_disconnect_controller.call_count, 2)
1985
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(
2000 self.libjuju.consume(
2001 "model_name", Offer(self.offer_url), self.provider_libjuju
2002 )
2003 )
2004 mock_consume.assert_called_once()
2005 mock_disconnect_model.assert_called_once()
2006 self.assertEqual(mock_disconnect_controller.call_count, 2)
2007
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(
2022 self.libjuju.consume(
2023 "model_name", Offer(self.offer_url), self.provider_libjuju
2024 )
2025 )
2026 mock_consume.assert_called_once()
2027 mock_disconnect_model.assert_called_once()
2028 self.assertEqual(mock_disconnect_controller.call_count, 2)
2029
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()
2040 mock_consume.side_effect = juju.errors.JujuAPIError(
2041 {"error": "", "response": "", "request-id": ""}
2042 )
2043
2044 with self.assertRaises(juju.errors.JujuAPIError):
2045 self.loop.run_until_complete(
2046 self.libjuju.consume(
2047 "model_name", Offer(self.offer_url), self.provider_libjuju
2048 )
2049 )
2050 mock_consume.assert_called_once()
2051 mock_disconnect_model.assert_called_once()
2052 self.assertEqual(mock_disconnect_controller.call_count, 2)
2053
2054
2055 @asynctest.mock.patch("n2vc.libjuju.Libjuju.get_k8s_cloud_credential")
2056 @asynctest.mock.patch("n2vc.libjuju.Libjuju.add_cloud")
2057 class AddK8sTest(LibjujuTestCase):
2058 def setUp(self):
2059 super(AddK8sTest, self).setUp()
2060 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 }
2077
2078 def test_add_k8s(self, mock_add_cloud, mock_get_k8s_cloud_credential):
2079 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
2080 mock_add_cloud.assert_called_once()
2081 mock_get_k8s_cloud_credential.assert_called_once()
2082
2083 def test_add_k8s_exception(self, mock_add_cloud, mock_get_k8s_cloud_credential):
2084 mock_add_cloud.side_effect = Exception()
2085 with self.assertRaises(Exception):
2086 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
2087 mock_add_cloud.assert_called_once()
2088 mock_get_k8s_cloud_credential.assert_called_once()
2089
2090 def test_add_k8s_missing_name(self, mock_add_cloud, mock_get_k8s_cloud_credential):
2091 self._add_k8s_args["name"] = ""
2092 with self.assertRaises(Exception):
2093 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
2094 mock_add_cloud.assert_not_called()
2095
2096 def test_add_k8s_missing_storage_name(
2097 self, mock_add_cloud, mock_get_k8s_cloud_credential
2098 ):
2099 self._add_k8s_args["storage_class"] = ""
2100 with self.assertRaises(Exception):
2101 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
2102 mock_add_cloud.assert_not_called()
2103
2104 def test_add_k8s_missing_configuration_keys(
2105 self, mock_add_cloud, mock_get_k8s_cloud_credential
2106 ):
2107 self._add_k8s_args["configuration"] = None
2108 with self.assertRaises(Exception):
2109 self.loop.run_until_complete(self.libjuju.add_k8s(**self._add_k8s_args))
2110 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")
2117 class 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")
2201 class RemoveCloudTest(LibjujuTestCase):
2202 def setUp(self):
2203 super(RemoveCloudTest, self).setUp()
2204
2205 def test_remove_cloud(
2206 self,
2207 mock_remove_cloud,
2208 mock_disconnect_controller,
2209 mock_get_controller,
2210 ):
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(
2218 self,
2219 mock_remove_cloud,
2220 mock_disconnect_controller,
2221 mock_get_controller,
2222 ):
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()
2230
2231
2232 @asynctest.mock.patch("kubernetes.client.configuration.Configuration")
2233 class GetK8sCloudCredentials(LibjujuTestCase):
2234 def setUp(self):
2235 super(GetK8sCloudCredentials, self).setUp()
2236 self.cert_data = "cert"
2237 self.token = "token"
2238
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
2247 self.token = None
2248 self.cert_data = None
2249 try:
2250 _ = self.libjuju.get_k8s_cloud_credential(
2251 mock_configuration,
2252 self.cert_data,
2253 self.token,
2254 )
2255 except JujuInvalidK8sConfiguration as e:
2256 exception_raised = True
2257 self.assertEqual(
2258 e.message,
2259 "authentication method not supported",
2260 )
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
2269 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 )
2276 self.assertEqual(
2277 credential,
2278 juju.client._definitions.CloudCredential(
2279 attrs={"username": "admin", "password": "admin"}, auth_type="userpass"
2280 ),
2281 )
2282
2283 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
2307 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
2313 self.token = None
2314 self.cert_data = None
2315 with patch.object(self.libjuju.log, "debug") as mock_debug:
2316 credential = self.libjuju.get_k8s_cloud_credential(
2317 mock_configuration,
2318 self.cert_data,
2319 self.token,
2320 )
2321 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
2331 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
2341 credential = self.libjuju.get_k8s_cloud_credential(
2342 mock_configuration,
2343 self.cert_data,
2344 self.token,
2345 )
2346 self.assertEqual(
2347 credential,
2348 juju.client._definitions.CloudCredential(
2349 attrs={"ClientCertificateData": self.cert_data, "Token": self.token},
2350 auth_type="certificate",
2351 ),
2352 )
2353
2354 # 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 # )
2377
2378 # @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)
2402
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:
2412 _ = self.libjuju.get_k8s_cloud_credential(
2413 mock_configuration,
2414 self.cert_data,
2415 self.token,
2416 )
2417 except JujuInvalidK8sConfiguration as e:
2418 exception_raised = True
2419 self.assertEqual(
2420 e.message,
2421 "Cannot set both token and user/pass",
2422 )
2423 self.assertTrue(exception_raised)
2424
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")
2432 class 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()
2449 self.loop.run_until_complete(self.libjuju.scale_application("model", "app", 2))
2450 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(
2467 self.libjuju.scale_application("model", "app", 2)
2468 )
2469 mock_disconnect_controller.assert_called()
2470 mock_disconnect_model.assert_called()
2471
2472 def test_exception(
2473 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,
2480 ):
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(
2485 self.libjuju.scale_application("model", "app", 2, total_timeout=0)
2486 )
2487 mock_disconnect_controller.assert_called_once()
2488
2489
2490 @asynctest.mock.patch("n2vc.libjuju.Libjuju._get_application")
2491 class GetUnitNumberTest(LibjujuTestCase):
2492 def setUp(self):
2493 super(GetUnitNumberTest, self).setUp()
2494
2495 def test_successful_get_unit_number(
2496 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)
2512
2513
2514 @asynctest.mock.patch("juju.model.Model.machines", new_callable=asynctest.PropertyMock)
2515 class 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
2548 class 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")
2566 class 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")
2625 class 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")
2720 class 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()