Merge ~smigiel-dariusz/cloud-upgrade-planner:unittest_cleanup into cloud-upgrade-planner:master

Proposed by Dariusz Smigiel
Status: Merged
Approved by: James Troup
Approved revision: 93a05272816eadbdd7e411c74f905bd7a988e19f
Merged at revision: b20a7cb2f155a4759068e0cc6f65c9df94df0058
Proposed branch: ~smigiel-dariusz/cloud-upgrade-planner:unittest_cleanup
Merge into: cloud-upgrade-planner:master
Diff against target: 1076 lines (+456/-440)
9 files modified
cloud_upgrade_planner/cloud.py (+7/-15)
cloud_upgrade_planner/common.py (+6/-0)
setup.py (+2/-2)
tests/conftest.py (+202/-0)
tests/test_cli.py (+1/-1)
tests/test_cloud.py (+0/-4)
tests/test_common.py (+141/-0)
tests/test_openstack.py (+85/-0)
tests/test_ubuntu.py (+12/-418)
Reviewer Review Type Date Requested Status
BootStack Reviewers Pending
BootStack Reviewers Pending
Review via email: mp+409866@code.launchpad.net

Commit message

Unit tests clean up

Separated tests into corresponding files with a clear distinction wrt module functions.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision b20a7cb2f155a4759068e0cc6f65c9df94df0058

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloud_upgrade_planner/cloud.py b/cloud_upgrade_planner/cloud.py
2index 3f11723..e29bc4d 100644
3--- a/cloud_upgrade_planner/cloud.py
4+++ b/cloud_upgrade_planner/cloud.py
5@@ -43,6 +43,7 @@ from fabric2 import Config, Connection
6 from paramiko.ssh_exception import SSHException
7
8 from cloud_upgrade_planner.logging import Logger
9+from cloud_upgrade_planner.common import parse_yaml
10
11
12 class Cloud:
13@@ -128,15 +129,6 @@ class Cloud:
14 return None
15 return result.stdout
16
17- def run_unit_command(self, target, command):
18- """Run a command on a Juju unit and return the output."""
19-
20- @staticmethod
21- def parse_yaml(yaml_string):
22- """Parse YAML using PyYAML."""
23- data = yaml.safe_load_all(yaml_string)
24- return list(data)
25-
26 def get_openstack_servers(self, controller, model, parsed_data=None):
27 """Get a list of openstack servers."""
28 if self.cloud_type != "openstack":
29@@ -172,7 +164,7 @@ class Cloud:
30 for host in range(num_nova_compute_units // 3)
31 for _ in range(24)
32 ])
33- servers = self.parse_yaml(server_data)
34+ servers = parse_yaml(server_data)
35
36 self.logger.info("[%s] Processing openstack server data", self.name)
37
38@@ -187,7 +179,7 @@ class Cloud:
39 """Get a list of Juju controllers."""
40 controller_output = self.run_command("juju controllers --format yaml")
41 if controller_output:
42- controllers = self.parse_yaml(controller_output)
43+ controllers = parse_yaml(controller_output)
44
45 if len(controllers) > 0:
46 self.logger.debug("Juju controller list: {}".format(controllers[0]))
47@@ -221,7 +213,7 @@ class Cloud:
48 "juju models -c {} --format yaml".format(controller)
49 )
50 self.logger.debug("Getting models from: {}".format(models_data))
51- models = self.parse_yaml(models_data)
52+ models = parse_yaml(models_data)
53 if len(models) > 0:
54 if "models" in models[0]:
55 for model in models[0]["models"]:
56@@ -259,7 +251,7 @@ class Cloud:
57 status_data = self.run_command(
58 "juju status -m {}:{} --format yaml".format(controller, model)
59 )
60- status = self.parse_yaml(status_data)
61+ status = parse_yaml(status_data)
62
63 self.logger.info(
64 "[{}] Processing Juju status for model {} on controller {}".format(
65@@ -339,7 +331,7 @@ class Cloud:
66 bundle_data = self.run_command(
67 "juju export-bundle -m {}:{}".format(controller, model)
68 )
69- bundles = self.parse_yaml(bundle_data)
70+ bundles = parse_yaml(bundle_data)
71
72 self.logger.info(
73 "[{}] Processing Juju bundle export for model {} on controller {}".format(
74@@ -438,4 +430,4 @@ class Cloud:
75 # process bundle
76 self.get_juju_bundle(self.controller, self.model, parsed_data=juju_bundle)
77 # process openstack_servers
78- self.get_openstack_servers(self.controller, self.model, parsed_data=openstack_servers)
79\ No newline at end of file
80+ self.get_openstack_servers(self.controller, self.model, parsed_data=openstack_servers)
81diff --git a/cloud_upgrade_planner/common.py b/cloud_upgrade_planner/common.py
82index a31016e..e076f09 100644
83--- a/cloud_upgrade_planner/common.py
84+++ b/cloud_upgrade_planner/common.py
85@@ -3,6 +3,12 @@ import sys
86 import yaml
87
88
89+def parse_yaml(yaml_string):
90+ """Parse YAML using PyYAML."""
91+ data = yaml.safe_load_all(yaml_string)
92+ return list(data)
93+
94+
95 def find_units_for_app(app, model_apps):
96 return list(model_apps[app]['units'].keys())
97
98diff --git a/cloud_upgrade_planner/openstack_managed_upgrade.py b/cloud_upgrade_planner/openstack.py
99similarity index 100%
100rename from cloud_upgrade_planner/openstack_managed_upgrade.py
101rename to cloud_upgrade_planner/openstack.py
102diff --git a/cloud_upgrade_planner/ubuntu_series_upgrade_for_openstack.py b/cloud_upgrade_planner/ubuntu.py
103similarity index 100%
104rename from cloud_upgrade_planner/ubuntu_series_upgrade_for_openstack.py
105rename to cloud_upgrade_planner/ubuntu.py
106diff --git a/setup.py b/setup.py
107index a9ecfa7..83f1692 100644
108--- a/setup.py
109+++ b/setup.py
110@@ -48,8 +48,8 @@ setuptools.setup(
111 "console_scripts": [
112 "cloud-upgrade-planner=cloud_upgrade_planner.cli:main",
113 "update-charm-revisions=cloud_upgrade_planner.cli:refresh",
114- "ubuntu-series-upgrade-for-openstack=cloud_upgrade_planner.ubuntu_series_upgrade_for_openstack:main",
115- "openstack-managed-upgrade=cloud_upgrade_planner.openstack_managed_upgrade:main",
116+ "ubuntu-series-upgrade-for-openstack=cloud_upgrade_planner.ubuntu:main",
117+ "openstack-managed-upgrade=cloud_upgrade_planner.openstack:main",
118 ]
119 },
120 setup_requires=["setuptools_scm"],
121diff --git a/tests/conftest.py b/tests/conftest.py
122index ec178d4..ccedfc5 100644
123--- a/tests/conftest.py
124+++ b/tests/conftest.py
125@@ -12,6 +12,7 @@ import mock
126 import os
127 import pytest
128 import sys
129+import yaml
130
131 # bring in top level library to path
132 test_path = os.path.dirname(os.path.abspath(__file__))
133@@ -23,6 +24,207 @@ from cloud_upgrade_planner.cloud import Cloud
134 from cloud_upgrade_planner.charms import Charms
135
136
137+JUJU_FILE_MOCK = """
138+applications:
139+ aodh-mysql-router:
140+ can-upgrade-to: cs:mysql-router-11
141+ charm: cs:mysql-router-6
142+ easyrsa:
143+ can-upgrade-to: cs:~containers/easyrsa-408
144+ charm: cs:~containers/easyrsa-345
145+ units:
146+ easyrsa/0:
147+ leader: true
148+ machine: 7/lxd/3
149+ hacluster-neutron:
150+ can-upgrade-to: cs:hacluster-78
151+ charm: cs:hacluster-74
152+ hacluster-nova:
153+ can-upgrade-to: cs:hacluster-78
154+ charm: cs:hacluster-74
155+ hacluster-vault:
156+ can-upgrade-to: cs:hacluster-78
157+ charm: cs:hacluster-74
158+ landscape-client:
159+ charm: cs:landscape-client-35
160+ juju-lint:
161+ charm: local:focal/juju-lint-1
162+ units:
163+ juju-lint/0:
164+ leader: true
165+ machine: 8/lxd/15
166+ memcached:
167+ charm: cs:memcached-34
168+ units:
169+ memcached/1:
170+ leader: true
171+ machine: 5/lxd/4
172+ mysql-innodb-cluster:
173+ can-upgrade-to: cs:mysql-innodb-cluster-11
174+ charm: cs:mysql-innodb-cluster-5
175+ units:
176+ mysql-innodb-cluster/0:
177+ machine: 3/lxd/8
178+ subordinates:
179+ landscape-client/65: true
180+ mysql-innodb-cluster/1:
181+ leader: true
182+ machine: 4/lxd/8
183+ subordinates:
184+ landscape-client/66: true
185+ mysql-innodb-cluster/2:
186+ machine: 5/lxd/8
187+ subordinates:
188+ landscape-client/67: true
189+ neutron-api:
190+ can-upgrade-to: cs:neutron-api-299
191+ charm: cs:neutron-api-292
192+ units:
193+ neutron-api/0:
194+ machine: 6/lxd/5
195+ subordinates:
196+ hacluster-neutron/0: true
197+ landscape-client/69: true
198+ neutron-api-mysql-router/0: true
199+ neutron-api/1:
200+ machine: 7/lxd/6
201+ subordinates:
202+ hacluster-neutron/1: true
203+ landscape-client/68: true
204+ neutron-api-mysql-router/1: true
205+ neutron-api/2:
206+ leader: true
207+ machine: 8/lxd/5
208+ subordinates:
209+ hacluster-neutron/2: true
210+ landscape-client/70: true
211+ neutron-api-mysql-router/2: true
212+ nova-cloud-controller:
213+ can-upgrade-to: cs:nova-cloud-controller-358
214+ charm: cs:nova-cloud-controller-353
215+ units:
216+ nova-cloud-controller/0:
217+ machine: 6/lxd/6
218+ subordinates:
219+ hacluster-nova/0: true
220+ landscape-client/72: true
221+ nova-cloud-controller-mysql-router/0: true
222+ nova-cloud-controller/1:
223+ machine: 7/lxd/7
224+ subordinates:
225+ hacluster-nova/2: true
226+ landscape-client/73: true
227+ nova-cloud-controller-mysql-router/2: true
228+ nova-cloud-controller/2:
229+ leader: true
230+ machine: 8/lxd/6
231+ subordinates:
232+ hacluster-nova/1: true
233+ landscape-client/71: true
234+ nova-cloud-controller-mysql-router/1: true
235+ nova-cloud-controller-mysql-router:
236+ can-upgrade-to: cs:mysql-router-11
237+ charm: cs:mysql-router-6
238+ nova-compute-kvm:
239+ can-upgrade-to: cs:nova-compute-334
240+ charm: cs:nova-compute-325
241+ units:
242+ nova-compute-kvm/1:
243+ machine: '10'
244+ subordinates:
245+ landscape-client/19: true
246+ nova-compute-kvm/10:
247+ machine: '19'
248+ subordinates:
249+ landscape-client/10: true
250+ nova-compute-kvm/11:
251+ machine: '20'
252+ subordinates:
253+ landscape-client/20: true
254+ nova-compute-kvm/12:
255+ machine: '21'
256+ subordinates:
257+ landscape-client/11: true
258+ nova-compute-kvm/13:
259+ machine: '22'
260+ subordinates:
261+ landscape-client/4: true
262+ nova-compute-kvm/7:
263+ leader: true
264+ machine: '16'
265+ subordinates:
266+ landscape-client/7: true
267+ nova-compute-kvm/9:
268+ machine: '18'
269+ subordinates:
270+ landscape-client/16: true
271+ rabbitmq-server:
272+ can-upgrade-to: cs:~llama-charmers-next/rabbitmq-server-6
273+ charm: cs:~llama-charmers-next/rabbitmq-server-5
274+ units:
275+ rabbitmq-server/4:
276+ machine: 6/lxd/12
277+ rabbitmq-server/5:
278+ leader: true
279+ machine: 7/lxd/12
280+ rabbitmq-server/6:
281+ machine: 8/lxd/11
282+ vault:
283+ can-upgrade-to: cs:vault-50
284+ charm: cs:vault-44
285+ units:
286+ vault/0:
287+ machine: '0'
288+ subordinates:
289+ hacluster-vault/1: true
290+ landscape-client/24: true
291+ vault/1:
292+ leader: true
293+ machine: '1'
294+ subordinates:
295+ hacluster-vault/2: true
296+ landscape-client/23: true
297+ vault/2:
298+ machine: '2'
299+ subordinates:
300+ hacluster-vault/0: true
301+ landscape-client/22: true
302+"""
303+
304+SVC2CHARM = {
305+ "aodh-mysql-router": "mysql-router",
306+ "easyrsa": "easyrsa",
307+ "hacluster-neutron": "hacluster",
308+ "hacluster-nova": "hacluster",
309+ "hacluster-vault": "hacluster",
310+ "juju-lint": "juju-lint",
311+ "landscape-client": "landscape-client",
312+ "memcached": "memcached",
313+ "mysql-innodb-cluster": "mysql-innodb-cluster",
314+ "neutron-api": "neutron-api",
315+ "nova-cloud-controller": "nova-cloud-controller",
316+ "nova-cloud-controller-mysql-router": "mysql-router",
317+ "nova-compute-kvm": "nova-compute",
318+ "rabbitmq-server": "rabbitmq-server",
319+ "vault": "vault",
320+}
321+
322+
323+@pytest.fixture
324+def juju_file_mock():
325+ return JUJU_FILE_MOCK
326+
327+
328+@pytest.fixture
329+def juju_file_dict():
330+ return yaml.safe_load(JUJU_FILE_MOCK)["applications"]
331+
332+
333+@pytest.fixture
334+def svc2charm():
335+ return SVC2CHARM
336+
337+
338 @pytest.fixture
339 def mocked_pkg_resources(monkeypatch):
340 """Mock the pkg_resources library."""
341diff --git a/tests/test_cli.py b/tests/test_cli.py
342index dc482ce..156d417 100644
343--- a/tests/test_cli.py
344+++ b/tests/test_cli.py
345@@ -7,7 +7,7 @@ def test_plan_upgrade_from_file(cli):
346 expected_result = {
347 "groups": 26,
348 "total_task": 64,
349- "total_duration": 3276,
350+ "total_duration": 3234.0,
351 "total_data_down": 2,
352 "total_control_down": 810
353 }
354diff --git a/tests/test_cloud.py b/tests/test_cloud.py
355index 5476d3e..09a1e5b 100644
356--- a/tests/test_cloud.py
357+++ b/tests/test_cloud.py
358@@ -3,10 +3,6 @@ import subprocess
359 import mock
360
361
362-def test_cloud_parse_yaml(cloud):
363- assert cloud.parse_yaml("{}") == [{}]
364-
365-
366 def test_get_openstack_servers(cloud):
367 cloud.cloud_state["test-cloud"] = {
368 "models": {
369diff --git a/tests/test_common.py b/tests/test_common.py
370new file mode 100644
371index 0000000..c4b661b
372--- /dev/null
373+++ b/tests/test_common.py
374@@ -0,0 +1,141 @@
375+import pytest
376+from unittest.mock import patch, mock_open, call
377+import yaml
378+
379+from cloud_upgrade_planner import common as c
380+
381+
382+class TestCommon:
383+ @pytest.mark.parametrize(
384+ "app,expected",
385+ [
386+ ("memcached", "memcached/1"),
387+ ("mysql-innodb-cluster", "mysql-innodb-cluster/1"),
388+ ("neutron-api", "neutron-api/2"),
389+ ],
390+ )
391+ def test_find_leader_for_app(self, app, expected, juju_file_dict):
392+ output = c.find_leader_for_app(app, juju_file_dict)
393+ assert output == expected
394+
395+ def test_get_model_apps_missing_file(self):
396+ with pytest.raises(SystemExit):
397+ c.get_model_apps("")
398+
399+ def test_get_model_apps_provided_file(self, juju_file_mock, juju_file_dict):
400+ with patch("builtins.open", new_callable=mock_open, read_data=juju_file_mock):
401+ output = c.get_model_apps("test")
402+ assert output == juju_file_dict
403+
404+ @pytest.mark.parametrize(
405+ "charms,expected",
406+ [
407+ (
408+ ["mysql-router", "nova-compute", "hacluster", "vault"],
409+ [
410+ "aodh-mysql-router",
411+ "nova-cloud-controller-mysql-router",
412+ "nova-compute-kvm",
413+ "hacluster-neutron",
414+ "hacluster-nova",
415+ "hacluster-vault",
416+ "vault",
417+ ],
418+ ),
419+ (["non-existing-charm"], []),
420+ ],
421+ )
422+ def test_find_charms_apps_in_model(self, charms, expected, juju_file_dict, svc2charm):
423+ output = c.find_charms_apps_in_model(charms, juju_file_dict, svc2charm)
424+ assert output == expected
425+
426+ @pytest.mark.parametrize(
427+ "app,expected",
428+ [
429+ (
430+ "nova-compute-kvm",
431+ [
432+ "nova-compute-kvm/1",
433+ "nova-compute-kvm/10",
434+ "nova-compute-kvm/11",
435+ "nova-compute-kvm/12",
436+ "nova-compute-kvm/13",
437+ "nova-compute-kvm/7",
438+ "nova-compute-kvm/9",
439+ ],
440+ ),
441+ (
442+ "mysql-innodb-cluster",
443+ [
444+ "mysql-innodb-cluster/0",
445+ "mysql-innodb-cluster/1",
446+ "mysql-innodb-cluster/2",
447+ ],
448+ ),
449+ ],
450+ )
451+ def test_find_units_for_app(self, app, expected, juju_file_dict):
452+ output = c.find_units_for_app(app, juju_file_dict)
453+ assert output == expected
454+
455+ @pytest.mark.parametrize(
456+ "charm,expected",
457+ [
458+ ("neutron-api", ["neutron-api"]),
459+ ("nova-compute", ["nova-compute-kvm"]),
460+ (
461+ "mysql-router",
462+ ["aodh-mysql-router", "nova-cloud-controller-mysql-router"],
463+ ),
464+ ],
465+ )
466+ def test_find_apps_from_charm(self, charm, expected, juju_file_dict):
467+ output = c.find_apps_from_charm(charm, juju_file_dict)
468+ assert output == expected
469+
470+ def test_find_units_for_app_not_in_phase(self, juju_file_dict):
471+ with pytest.raises(KeyError):
472+ c.find_units_for_app("nova-cloud-controller-mysql-router", juju_file_dict)
473+
474+ def test_find_units_for_app_missing(self, juju_file_dict):
475+ with pytest.raises(KeyError):
476+ c.find_units_for_app("appx", juju_file_dict)
477+
478+ @pytest.mark.parametrize(
479+ "app,unit,expected",
480+ [
481+ ("nova-compute-kvm", "nova-compute-kvm/9", "18"),
482+ ("memcached", "memcached/1", "5/lxd/4"),
483+ ],
484+ )
485+ def test_find_machine_from_unit(self, app, unit, expected, juju_file_dict):
486+ output = c.find_machine_from_unit(app, unit, juju_file_dict)
487+ assert output == expected
488+
489+ def test_render_app_to_charm_dict(self, juju_file_dict, svc2charm):
490+ output = c.render_app_to_charm_dict(juju_file_dict)
491+ assert output == svc2charm
492+
493+ @pytest.mark.parametrize(
494+ "app,expected",
495+ [
496+ ("aodh-mysql-router", True),
497+ ("memcached", False),
498+ ],
499+ )
500+ def test_available_upgrade(self, app, expected, juju_file_dict):
501+ output = c.available_upgrade(app, juju_file_dict)
502+ assert output == expected
503+
504+ @pytest.mark.parametrize(
505+ "app,unit,expected",
506+ [
507+ ("nova-cloud-controller", "nova-cloud-controller/1", "hacluster-nova/2"),
508+ ("neutron-api", "neutron-api/2", "hacluster-neutron/2"),
509+ ("nova-compute-kvm", "nova-compute-kvm/1", None), # missing HA
510+ ("memcached", "memcached/1", None), # missing subordinates
511+ ],
512+ )
513+ def test_find_hacluster_for_unit(self, app, unit, expected, juju_file_dict):
514+ output = c.find_hacluster_for_unit(app, unit, juju_file_dict)
515+ assert output == expected
516diff --git a/tests/test_openstack.py b/tests/test_openstack.py
517new file mode 100644
518index 0000000..82283fb
519--- /dev/null
520+++ b/tests/test_openstack.py
521@@ -0,0 +1,85 @@
522+from unittest.mock import patch, mock_open, call
523+
524+from cloud_upgrade_planner import openstack as o
525+
526+
527+class TestOpenstackManagedUpgrade:
528+ @patch("builtins.print")
529+ @patch(
530+ "cloud_upgrade_planner.common.find_units_for_app",
531+ return_value=["neutron-api/0", "neutron-api/1", "neutron-api/2"],
532+ )
533+ @patch(
534+ "cloud_upgrade_planner.common.find_leader_for_app", return_value="neutron-api/2"
535+ )
536+ def test_plan_action_managed_phase_app(self, mock_leader, mock_units, mock_stdout, juju_file_dict):
537+ o.plan_action_managed_phase_app("test-app", juju_file_dict, "test-release")
538+ calls = [
539+ call(
540+ " juju config test-app action-managed-upgrade=true openstack-origin=test-release"
541+ ),
542+ call(" {}".format(o.WATCH_COMMAND)),
543+ call(" juju run-action --wait neutron-api/2 openstack-upgrade"),
544+ call(" juju run-action --wait neutron-api/0 openstack-upgrade"),
545+ call(" juju run-action --wait neutron-api/1 openstack-upgrade"),
546+ ]
547+ assert mock_stdout.mock_calls == calls
548+
549+ @patch("builtins.print")
550+ def test_plan_charm_upgrade_local_path(self, mock_stdout, juju_file_dict, svc2charm):
551+ output = o.plan_charm_upgrade("juju-lint", juju_file_dict, svc2charm)
552+ calls = [
553+ call(
554+ "\n WARNING!!! Application juju-lint has local charm path local:focal/juju-lint-1\n Suggesting switch to latest promulgated cs: version.\n Please ensure updates in local charm are in upstream charm before\n running upgrade-charm on this application.\n "
555+ ),
556+ call(" juju upgrade-charm juju-lint --switch cs:juju-lint"),
557+ call(
558+ " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
559+ ),
560+ ]
561+ assert mock_stdout.mock_calls == calls
562+
563+ @patch("builtins.print")
564+ def test_plan_charm_upgrade_cs_path(self, mock_stdout, juju_file_dict, svc2charm):
565+ output = o.plan_charm_upgrade("neutron-api", juju_file_dict, svc2charm)
566+ calls = [
567+ call(" juju upgrade-charm neutron-api"),
568+ call(
569+ " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
570+ ),
571+ ]
572+ assert mock_stdout.mock_calls == calls
573+
574+ @patch("builtins.print")
575+ def test_plan_charm_upgrade_tilde(self, mock_stdout, juju_file_dict, svc2charm):
576+ output = o.plan_charm_upgrade("easyrsa", juju_file_dict, svc2charm)
577+ calls = [
578+ call(
579+ "\n WARNING!!! Application easyrsa from unrecognized path cs:~containers/easyrsa-345\n Suggesting switch to latest promulgated cs: version.\n Please ensure updates in the above branched charm are in the latest\n promulgated charm before running upgrade-charm on this application.\n "
580+ ),
581+ call(" juju upgrade-charm easyrsa --switch cs:easyrsa"),
582+ call(
583+ " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
584+ ),
585+ ]
586+ assert mock_stdout.mock_calls == calls
587+
588+ @patch("builtins.print")
589+ def test_plan_charm_upgrade_deferred(self, mock_stdout, juju_file_mock, juju_file_dict, svc2charm):
590+ with patch("builtins.open", new_callable=mock_open, read_data=juju_file_mock) as defer_file:
591+ calls = [
592+ call(
593+ "\n WARNING!!! Application rabbitmq-server from unrecognized path cs:~llama-charmers-next/rabbitmq-server-5\n Suggesting switch to latest promulgated cs: version.\n Please ensure updates in the above branched charm are in the latest\n promulgated charm before running upgrade-charm on this application.\n "
594+ ),
595+ call("rabbitmq-server:\n enable-auto-restarts: False", file=defer_file()),
596+ call(
597+ " juju upgrade-charm rabbitmq-server --switch cs:rabbitmq-server --config deferred_restart_config_rabbitmq-server.yaml"
598+ ),
599+ call(
600+ " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
601+ ),
602+ ]
603+ output = o.plan_charm_upgrade("rabbitmq-server", juju_file_dict, svc2charm)
604+ assert mock_stdout.mock_calls == calls
605+
606+
607diff --git a/tests/test_ubuntu_series_upgrade_for_openstack.py b/tests/test_ubuntu.py
608similarity index 50%
609rename from tests/test_ubuntu_series_upgrade_for_openstack.py
610rename to tests/test_ubuntu.py
611index 8d80437..72307dd 100644
612--- a/tests/test_ubuntu_series_upgrade_for_openstack.py
613+++ b/tests/test_ubuntu.py
614@@ -1,413 +1,7 @@
615 import pytest
616 from unittest.mock import patch, mock_open, call
617-import yaml
618
619-from cloud_upgrade_planner import common as c
620-from cloud_upgrade_planner import openstack_managed_upgrade as o
621-from cloud_upgrade_planner import ubuntu_series_upgrade_for_openstack as u
622-
623-
624-JUJU_FILE_MOCK = """
625-applications:
626- aodh-mysql-router:
627- can-upgrade-to: cs:mysql-router-11
628- charm: cs:mysql-router-6
629- easyrsa:
630- can-upgrade-to: cs:~containers/easyrsa-408
631- charm: cs:~containers/easyrsa-345
632- units:
633- easyrsa/0:
634- leader: true
635- machine: 7/lxd/3
636- hacluster-neutron:
637- can-upgrade-to: cs:hacluster-78
638- charm: cs:hacluster-74
639- hacluster-nova:
640- can-upgrade-to: cs:hacluster-78
641- charm: cs:hacluster-74
642- hacluster-vault:
643- can-upgrade-to: cs:hacluster-78
644- charm: cs:hacluster-74
645- landscape-client:
646- charm: cs:landscape-client-35
647- juju-lint:
648- charm: local:focal/juju-lint-1
649- units:
650- juju-lint/0:
651- leader: true
652- machine: 8/lxd/15
653- memcached:
654- charm: cs:memcached-34
655- units:
656- memcached/1:
657- leader: true
658- machine: 5/lxd/4
659- mysql-innodb-cluster:
660- can-upgrade-to: cs:mysql-innodb-cluster-11
661- charm: cs:mysql-innodb-cluster-5
662- units:
663- mysql-innodb-cluster/0:
664- machine: 3/lxd/8
665- subordinates:
666- landscape-client/65: true
667- mysql-innodb-cluster/1:
668- leader: true
669- machine: 4/lxd/8
670- subordinates:
671- landscape-client/66: true
672- mysql-innodb-cluster/2:
673- machine: 5/lxd/8
674- subordinates:
675- landscape-client/67: true
676- neutron-api:
677- can-upgrade-to: cs:neutron-api-299
678- charm: cs:neutron-api-292
679- units:
680- neutron-api/0:
681- machine: 6/lxd/5
682- subordinates:
683- hacluster-neutron/0: true
684- landscape-client/69: true
685- neutron-api-mysql-router/0: true
686- neutron-api/1:
687- machine: 7/lxd/6
688- subordinates:
689- hacluster-neutron/1: true
690- landscape-client/68: true
691- neutron-api-mysql-router/1: true
692- neutron-api/2:
693- leader: true
694- machine: 8/lxd/5
695- subordinates:
696- hacluster-neutron/2: true
697- landscape-client/70: true
698- neutron-api-mysql-router/2: true
699- nova-cloud-controller:
700- can-upgrade-to: cs:nova-cloud-controller-358
701- charm: cs:nova-cloud-controller-353
702- units:
703- nova-cloud-controller/0:
704- machine: 6/lxd/6
705- subordinates:
706- hacluster-nova/0: true
707- landscape-client/72: true
708- nova-cloud-controller-mysql-router/0: true
709- nova-cloud-controller/1:
710- machine: 7/lxd/7
711- subordinates:
712- hacluster-nova/2: true
713- landscape-client/73: true
714- nova-cloud-controller-mysql-router/2: true
715- nova-cloud-controller/2:
716- leader: true
717- machine: 8/lxd/6
718- subordinates:
719- hacluster-nova/1: true
720- landscape-client/71: true
721- nova-cloud-controller-mysql-router/1: true
722- nova-cloud-controller-mysql-router:
723- can-upgrade-to: cs:mysql-router-11
724- charm: cs:mysql-router-6
725- nova-compute-kvm:
726- can-upgrade-to: cs:nova-compute-334
727- charm: cs:nova-compute-325
728- units:
729- nova-compute-kvm/1:
730- machine: '10'
731- subordinates:
732- landscape-client/19: true
733- nova-compute-kvm/10:
734- machine: '19'
735- subordinates:
736- landscape-client/10: true
737- nova-compute-kvm/11:
738- machine: '20'
739- subordinates:
740- landscape-client/20: true
741- nova-compute-kvm/12:
742- machine: '21'
743- subordinates:
744- landscape-client/11: true
745- nova-compute-kvm/13:
746- machine: '22'
747- subordinates:
748- landscape-client/4: true
749- nova-compute-kvm/7:
750- leader: true
751- machine: '16'
752- subordinates:
753- landscape-client/7: true
754- nova-compute-kvm/9:
755- machine: '18'
756- subordinates:
757- landscape-client/16: true
758- rabbitmq-server:
759- can-upgrade-to: cs:~llama-charmers-next/rabbitmq-server-6
760- charm: cs:~llama-charmers-next/rabbitmq-server-5
761- units:
762- rabbitmq-server/4:
763- machine: 6/lxd/12
764- rabbitmq-server/5:
765- leader: true
766- machine: 7/lxd/12
767- rabbitmq-server/6:
768- machine: 8/lxd/11
769- vault:
770- can-upgrade-to: cs:vault-50
771- charm: cs:vault-44
772- units:
773- vault/0:
774- machine: '0'
775- subordinates:
776- hacluster-vault/1: true
777- landscape-client/24: true
778- vault/1:
779- leader: true
780- machine: '1'
781- subordinates:
782- hacluster-vault/2: true
783- landscape-client/23: true
784- vault/2:
785- machine: '2'
786- subordinates:
787- hacluster-vault/0: true
788- landscape-client/22: true
789-"""
790-JUJU_FILE_DICT = yaml.safe_load(JUJU_FILE_MOCK)["applications"]
791-
792-SVC2CHARM = {
793- "aodh-mysql-router": "mysql-router",
794- "easyrsa": "easyrsa",
795- "hacluster-neutron": "hacluster",
796- "hacluster-nova": "hacluster",
797- "hacluster-vault": "hacluster",
798- "juju-lint": "juju-lint",
799- "landscape-client": "landscape-client",
800- "memcached": "memcached",
801- "mysql-innodb-cluster": "mysql-innodb-cluster",
802- "neutron-api": "neutron-api",
803- "nova-cloud-controller": "nova-cloud-controller",
804- "nova-cloud-controller-mysql-router": "mysql-router",
805- "nova-compute-kvm": "nova-compute",
806- "rabbitmq-server": "rabbitmq-server",
807- "vault": "vault",
808-}
809-
810-
811-class TestCommon:
812- @pytest.mark.parametrize(
813- "app,expected",
814- [
815- ("memcached", "memcached/1"),
816- ("mysql-innodb-cluster", "mysql-innodb-cluster/1"),
817- ("neutron-api", "neutron-api/2"),
818- ],
819- )
820- def test_find_leader_for_app(self, app, expected):
821- output = c.find_leader_for_app(app, JUJU_FILE_DICT)
822- assert output == expected
823-
824- def test_get_model_apps_missing_file(self):
825- with pytest.raises(SystemExit):
826- c.get_model_apps("")
827-
828- @patch("builtins.open", new_callable=mock_open, read_data=JUJU_FILE_MOCK)
829- def test_get_model_apps_provided_file(self, juju_file):
830- output = c.get_model_apps("test")
831- assert output == JUJU_FILE_DICT
832-
833- @pytest.mark.parametrize(
834- "charms,expected",
835- [
836- (
837- ["mysql-router", "nova-compute", "hacluster", "vault"],
838- [
839- "aodh-mysql-router",
840- "nova-cloud-controller-mysql-router",
841- "nova-compute-kvm",
842- "hacluster-neutron",
843- "hacluster-nova",
844- "hacluster-vault",
845- "vault",
846- ],
847- ),
848- (["non-existing-charm"], []),
849- ],
850- )
851- def test_find_charms_apps_in_model(self, charms, expected):
852- output = c.find_charms_apps_in_model(charms, JUJU_FILE_DICT, SVC2CHARM)
853- assert output == expected
854-
855- @pytest.mark.parametrize(
856- "app,expected",
857- [
858- (
859- "nova-compute-kvm",
860- [
861- "nova-compute-kvm/1",
862- "nova-compute-kvm/10",
863- "nova-compute-kvm/11",
864- "nova-compute-kvm/12",
865- "nova-compute-kvm/13",
866- "nova-compute-kvm/7",
867- "nova-compute-kvm/9",
868- ],
869- ),
870- (
871- "mysql-innodb-cluster",
872- [
873- "mysql-innodb-cluster/0",
874- "mysql-innodb-cluster/1",
875- "mysql-innodb-cluster/2",
876- ],
877- ),
878- ],
879- )
880- def test_find_units_for_app(self, app, expected):
881- output = c.find_units_for_app(app, JUJU_FILE_DICT)
882- assert output == expected
883-
884- @pytest.mark.parametrize(
885- "charm,expected",
886- [
887- ("neutron-api", ["neutron-api"]),
888- ("nova-compute", ["nova-compute-kvm"]),
889- (
890- "mysql-router",
891- ["aodh-mysql-router", "nova-cloud-controller-mysql-router"],
892- ),
893- ],
894- )
895- def test_find_apps_from_charm(self, charm, expected):
896- output = c.find_apps_from_charm(charm, JUJU_FILE_DICT)
897- assert output == expected
898-
899- def test_find_units_for_app_not_in_phase(self):
900- with pytest.raises(KeyError):
901- c.find_units_for_app("nova-cloud-controller-mysql-router", JUJU_FILE_DICT)
902-
903- def test_find_units_for_app_missing(self):
904- with pytest.raises(KeyError):
905- c.find_units_for_app("appx", JUJU_FILE_DICT)
906-
907- @pytest.mark.parametrize(
908- "app,unit,expected",
909- [
910- ("nova-compute-kvm", "nova-compute-kvm/9", "18"),
911- ("memcached", "memcached/1", "5/lxd/4"),
912- ],
913- )
914- def test_find_machine_from_unit(self, app, unit, expected):
915- output = c.find_machine_from_unit(app, unit, JUJU_FILE_DICT)
916- assert output == expected
917-
918- def test_render_app_to_charm_dict(self):
919- output = c.render_app_to_charm_dict(JUJU_FILE_DICT)
920- assert output == SVC2CHARM
921-
922- @pytest.mark.parametrize(
923- "app,expected",
924- [
925- ("aodh-mysql-router", True),
926- ("memcached", False),
927- ],
928- )
929- def test_available_upgrade(self, app, expected):
930- output = c.available_upgrade(app, JUJU_FILE_DICT)
931- assert output == expected
932-
933- @pytest.mark.parametrize(
934- "app,unit,expected",
935- [
936- ("nova-cloud-controller", "nova-cloud-controller/1", "hacluster-nova/2"),
937- ("neutron-api", "neutron-api/2", "hacluster-neutron/2"),
938- ("nova-compute-kvm", "nova-compute-kvm/1", None), # missing HA
939- ("memcached", "memcached/1", None), # missing subordinates
940- ],
941- )
942- def test_find_hacluster_for_unit(self, app, unit, expected):
943- output = c.find_hacluster_for_unit(app, unit, JUJU_FILE_DICT)
944- assert output == expected
945-
946-
947-class TestOpenstackManagedUpgrade:
948- @patch("builtins.print")
949- @patch(
950- "cloud_upgrade_planner.common.find_units_for_app",
951- return_value=["neutron-api/0", "neutron-api/1", "neutron-api/2"],
952- )
953- @patch(
954- "cloud_upgrade_planner.common.find_leader_for_app", return_value="neutron-api/2"
955- )
956- def test_plan_action_managed_phase_app(self, mock_leader, mock_units, mock_stdout):
957- o.plan_action_managed_phase_app("test-app", JUJU_FILE_DICT, "test-release")
958- calls = [
959- call(
960- " juju config test-app action-managed-upgrade=true openstack-origin=test-release"
961- ),
962- call(" {}".format(o.WATCH_COMMAND)),
963- call(" juju run-action --wait neutron-api/2 openstack-upgrade"),
964- call(" juju run-action --wait neutron-api/0 openstack-upgrade"),
965- call(" juju run-action --wait neutron-api/1 openstack-upgrade"),
966- ]
967- assert mock_stdout.mock_calls == calls
968-
969- @patch("builtins.print")
970- def test_plan_charm_upgrade_local_path(self, mock_stdout):
971- output = o.plan_charm_upgrade("juju-lint", JUJU_FILE_DICT, SVC2CHARM)
972- calls = [
973- call(
974- "\n WARNING!!! Application juju-lint has local charm path local:focal/juju-lint-1\n Suggesting switch to latest promulgated cs: version.\n Please ensure updates in local charm are in upstream charm before\n running upgrade-charm on this application.\n "
975- ),
976- call(" juju upgrade-charm juju-lint --switch cs:juju-lint"),
977- call(
978- " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
979- ),
980- ]
981- assert mock_stdout.mock_calls == calls
982-
983- @patch("builtins.print")
984- def test_plan_charm_upgrade_cs_path(self, mock_stdout):
985- output = o.plan_charm_upgrade("neutron-api", JUJU_FILE_DICT, SVC2CHARM)
986- calls = [
987- call(" juju upgrade-charm neutron-api"),
988- call(
989- " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
990- ),
991- ]
992- assert mock_stdout.mock_calls == calls
993-
994- @patch("builtins.print")
995- def test_plan_charm_upgrade_tilde(self, mock_stdout):
996- output = o.plan_charm_upgrade("easyrsa", JUJU_FILE_DICT, SVC2CHARM)
997- calls = [
998- call(
999- "\n WARNING!!! Application easyrsa from unrecognized path cs:~containers/easyrsa-345\n Suggesting switch to latest promulgated cs: version.\n Please ensure updates in the above branched charm are in the latest\n promulgated charm before running upgrade-charm on this application.\n "
1000- ),
1001- call(" juju upgrade-charm easyrsa --switch cs:easyrsa"),
1002- call(
1003- " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
1004- ),
1005- ]
1006- assert mock_stdout.mock_calls == calls
1007-
1008- @patch("builtins.print")
1009- @patch("builtins.open", new_callable=mock_open, read_data=JUJU_FILE_MOCK)
1010- def test_plan_charm_upgrade_deferred(self, defer_file, mock_stdout):
1011- output = o.plan_charm_upgrade("rabbitmq-server", JUJU_FILE_DICT, SVC2CHARM)
1012- calls = [
1013- call(
1014- "\n WARNING!!! Application rabbitmq-server from unrecognized path cs:~llama-charmers-next/rabbitmq-server-5\n Suggesting switch to latest promulgated cs: version.\n Please ensure updates in the above branched charm are in the latest\n promulgated charm before running upgrade-charm on this application.\n "
1015- ),
1016- call("rabbitmq-server:\n enable-auto-restarts: False", file=defer_file()),
1017- call(
1018- " juju upgrade-charm rabbitmq-server --switch cs:rabbitmq-server --config deferred_restart_config_rabbitmq-server.yaml"
1019- ),
1020- call(
1021- " watch \"juju status|egrep 'blocked|waiting|maint|error|hook|lost|executing'\"\n\n"
1022- ),
1023- ]
1024- assert mock_stdout.mock_calls == calls
1025+from cloud_upgrade_planner import ubuntu as u
1026
1027
1028 class TestUbuntuSeriesUpgrade:
1029@@ -415,36 +9,36 @@ class TestUbuntuSeriesUpgrade:
1030 with pytest.raises(SystemExit):
1031 u.main()
1032
1033- @patch("cloud_upgrade_planner.ubuntu_series_upgrade_for_openstack.plan_sequential")
1034- @patch("cloud_upgrade_planner.ubuntu_series_upgrade_for_openstack.plan_parallel")
1035+ @patch("cloud_upgrade_planner.ubuntu.plan_sequential")
1036+ @patch("cloud_upgrade_planner.ubuntu.plan_parallel")
1037 @patch(
1038 "cloud_upgrade_planner.common.find_apps_from_charm", return_value=["app"]
1039 )
1040- @patch("builtins.open", new_callable=mock_open, read_data=JUJU_FILE_MOCK)
1041 def test_main_with_juju_file(
1042- self, juju_file, mock_apps_from_charm, mock_parallel, mock_sequential
1043+ self, mock_apps_from_charm, mock_parallel, mock_sequential, juju_file_dict, juju_file_mock
1044 ):
1045- u.main()
1046+ with patch("builtins.open", new_callable=mock_open, read_data=juju_file_mock):
1047+ u.main()
1048
1049 charm_calls = []
1050 for phase in range(2):
1051 for app in u.PHASE_APPS[phase]:
1052- charm_calls.append(call(app, JUJU_FILE_DICT))
1053+ charm_calls.append(call(app, juju_file_dict))
1054 mock_apps_from_charm.assert_has_calls(charm_calls)
1055
1056 parallel_apps_no = len(u.PHASE_APPS[1])
1057 mock_parallel.assert_called_once_with(
1058- JUJU_FILE_DICT, ["app"] * parallel_apps_no, []
1059+ juju_file_dict, ["app"] * parallel_apps_no, []
1060 )
1061
1062 sequential_apps_no = len(u.PHASE_APPS[0]) + len(u.PHASE_APPS[2])
1063- seq_calls = [call(JUJU_FILE_DICT, "app", []) for _ in range(sequential_apps_no)]
1064+ seq_calls = [call(juju_file_dict, "app", []) for _ in range(sequential_apps_no)]
1065 mock_sequential.assert_has_calls(seq_calls)
1066
1067 @patch("builtins.print")
1068- @patch("builtins.open", new_callable=mock_open, read_data=JUJU_FILE_MOCK)
1069- def test_main_output(self, juju_file, mock_stdout):
1070- u.main()
1071+ def test_main_output(self, mock_stdout, juju_file_mock):
1072+ with patch("builtins.open", new_callable=mock_open, read_data=juju_file_mock):
1073+ u.main()
1074 calls = [
1075 call(u.PRE_UPGRADE_STEPS),
1076 call("Phase 0\n========"),

Subscribers

People subscribed via source and target branches