Merge ~gabrielcocenza/juju-lint:bug/1990885 into juju-lint:master
- Git
- lp:~gabrielcocenza/juju-lint
- bug/1990885
- Merge into master
Status: | Merged |
---|---|
Approved by: | Eric Chen |
Approved revision: | d11c04e32c8c1d34e3f58ade4bf6c6297686773d |
Merged at revision: | 52fe9bef72b5aec36de0f320e32c994d8fbd96ed |
Proposed branch: | ~gabrielcocenza/juju-lint:bug/1990885 |
Merge into: | juju-lint:master |
Diff against target: |
1083 lines (+661/-46) 10 files modified
jujulint/checks/hyper_converged.py (+38/-0) jujulint/lint.py (+31/-2) jujulint/model_input.py (+68/-3) tests/unit/conftest.py (+337/-1) tests/unit/test_hyper_converged.py (+32/-0) tests/unit/test_input.py (+61/-0) tests/unit/test_jujulint.py (+62/-4) tests/unit/test_relations.py (+2/-2) tests/unit/test_spaces.py (+29/-33) tox.ini (+1/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eric Chen | Approve | ||
🤖 prod-jenkaas-bootstack | continuous-integration | Approve | |
Mert Kirpici (community) | Approve | ||
JamesLin | Approve | ||
Review via email: mp+431602@code.launchpad.net |
Commit message
added check for hyper converged deployments
- warning for hyper converged deployments with Masakari
- added machines_to_apps field, filter_
and filter_
- created subdirectory form checks
- renamed module check_spaces.py to spaces.py
Closes-Bug: #1990885
Description of the change
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
PASSED: Continuous integration, rev:4634d824478
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
PASSED: Continuous integration, rev:895444691d9
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Mert Kirpici (mertkirpici) wrote : | # |
thanks Gabriel. I proposed one suggestion inline.
Eric Chen (eric-chen) wrote : | # |
Agree with Mert, please change it before we merge it. thanks!
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
PASSED: Continuous integration, rev:d11c04e32c8
https:/
Executed test runs:
SUCCESS: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Eric Chen (eric-chen) : | # |
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 52fe9bef72b5aec
Preview Diff
1 | diff --git a/jujulint/checks/hyper_converged.py b/jujulint/checks/hyper_converged.py |
2 | new file mode 100644 |
3 | index 0000000..0dd5da6 |
4 | --- /dev/null |
5 | +++ b/jujulint/checks/hyper_converged.py |
6 | @@ -0,0 +1,38 @@ |
7 | +#!/usr/bin/python3 |
8 | +"""Checks if nodes can be Hyper-Converged.""" |
9 | + |
10 | +from collections import defaultdict |
11 | +from typing import DefaultDict, Union |
12 | + |
13 | +from jujulint.model_input import JujuBundleFile, JujuStatusFile |
14 | + |
15 | + |
16 | +# see LP#1990885 |
17 | +def check_hyper_converged( |
18 | + input_file: Union[JujuBundleFile, JujuStatusFile] |
19 | +) -> DefaultDict[str, DefaultDict[str, set]]: |
20 | + """Check if other services are collocated with nova/osd with masakari. |
21 | + |
22 | + Hyperconvered is nova/osd collocated with openstack services. |
23 | + Masakari uses ha-cluster to monitor nodes. If the node is not responsive then the |
24 | + node is taken down. This is fine for nova/osd units, but if there are collocated |
25 | + with openstack services this can be problematic. |
26 | + |
27 | + |
28 | + :param input_file: mapped content of the input file. |
29 | + :type input_file: Union[JujuBundleFile, JujuStatusFile] |
30 | + :return: Services on lxds that are on nova/osd machines. |
31 | + :rtype: DefaultDict[str, DefaultDict[str, set]] |
32 | + """ |
33 | + hyper_converged_warning = defaultdict(lambda: defaultdict(set)) |
34 | + if "masakari" in input_file.charms: |
35 | + nova_machines = input_file.filter_machines_by_charm("nova-compute") |
36 | + ods_machines = input_file.filter_machines_by_charm("ceph-osd") |
37 | + nova_osd_machines = nova_machines.intersection(ods_machines) |
38 | + if nova_osd_machines: |
39 | + for machine in nova_osd_machines: |
40 | + lxds = input_file.filter_lxd_on_machine(machine) |
41 | + for lxd in lxds: |
42 | + apps = input_file.machines_to_apps[lxd] |
43 | + hyper_converged_warning[machine][lxd] = apps |
44 | + return hyper_converged_warning |
45 | diff --git a/jujulint/relations.py b/jujulint/checks/relations.py |
46 | similarity index 100% |
47 | rename from jujulint/relations.py |
48 | rename to jujulint/checks/relations.py |
49 | diff --git a/jujulint/check_spaces.py b/jujulint/checks/spaces.py |
50 | similarity index 100% |
51 | rename from jujulint/check_spaces.py |
52 | rename to jujulint/checks/spaces.py |
53 | diff --git a/jujulint/lint.py b/jujulint/lint.py |
54 | index b8fa535..f73b7ea 100755 |
55 | --- a/jujulint/lint.py |
56 | +++ b/jujulint/lint.py |
57 | @@ -33,11 +33,12 @@ import yaml |
58 | from attr import attrib, attrs |
59 | from dateutil import relativedelta |
60 | |
61 | +import jujulint.checks.hyper_converged as hyper_converged |
62 | import jujulint.util as utils |
63 | -from jujulint.check_spaces import Relation, find_space_mismatches |
64 | +from jujulint.checks.relations import RelationError, RelationsRulesBootStrap |
65 | +from jujulint.checks.spaces import Relation, find_space_mismatches |
66 | from jujulint.logging import Logger |
67 | from jujulint.model_input import input_handler |
68 | -from jujulint.relations import RelationError, RelationsRulesBootStrap |
69 | |
70 | VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte", "search") |
71 | VALID_LOG_LEVEL = { |
72 | @@ -757,6 +758,33 @@ class Linter: |
73 | } |
74 | ) |
75 | |
76 | + def check_hyper_converged(self, input_file): |
77 | + """Check hyper converged deployments. |
78 | + |
79 | + :param input_file: mapped content of the input file. |
80 | + :type input_file: Union[JujuBundleFile, JujuStatusFile] |
81 | + """ |
82 | + hyper_converged_warning = hyper_converged.check_hyper_converged(input_file) |
83 | + |
84 | + if hyper_converged_warning: |
85 | + for machine in hyper_converged_warning: |
86 | + for lxd in hyper_converged_warning[machine]: |
87 | + self.message_handler( |
88 | + { |
89 | + "id": "hyper-converged-masakari", |
90 | + "tags": ["hyper-converged", "masakari"], |
91 | + "message": ( |
92 | + "Deployment has Masakari and the machine: '{}' " |
93 | + "has nova/osd and the lxd: '{}' with those services {}" |
94 | + ).format( |
95 | + machine, |
96 | + lxd, |
97 | + sorted(list(hyper_converged_warning[machine][lxd])), |
98 | + ), |
99 | + }, |
100 | + log_level=logging.WARNING, |
101 | + ) |
102 | + |
103 | def check_charms_ops_mandatory(self, charm): |
104 | """ |
105 | Check if a mandatory ops charms is present in the model. |
106 | @@ -1352,6 +1380,7 @@ class Linter: |
107 | |
108 | self.check_subs(parsed_yaml["machines"]) |
109 | self.check_relations(input_file) |
110 | + self.check_hyper_converged(input_file) |
111 | self.check_charms() |
112 | |
113 | if "relations" in parsed_yaml: |
114 | diff --git a/jujulint/model_input.py b/jujulint/model_input.py |
115 | index a14551c..8233ebb 100644 |
116 | --- a/jujulint/model_input.py |
117 | +++ b/jujulint/model_input.py |
118 | @@ -25,6 +25,7 @@ class BaseFile: |
119 | app_to_charm: Dict = field(default_factory=dict) |
120 | charm_to_app: defaultdict[set] = field(default_factory=lambda: defaultdict(set)) |
121 | apps_to_machines: defaultdict[set] = field(default_factory=lambda: defaultdict(set)) |
122 | + machines_to_apps: defaultdict[set] = field(default_factory=lambda: defaultdict(set)) |
123 | |
124 | def __post_init__(self): |
125 | """Dunder method to map file after instantiating.""" |
126 | @@ -124,6 +125,20 @@ class BaseFile: |
127 | else set() |
128 | ) |
129 | |
130 | + def filter_machines_by_charm(self, charm: str) -> Set: |
131 | + """Filter machines that has a specific charm. |
132 | + |
133 | + :param charm: Charm name. |
134 | + :type charm: str |
135 | + :return: Machines that contains the charm. |
136 | + :rtype: Set |
137 | + """ |
138 | + charm_machines = set() |
139 | + charm_apps = self.charm_to_app[charm] |
140 | + for charm_app in charm_apps: |
141 | + charm_machines.update(self.apps_to_machines[charm_app]) |
142 | + return charm_machines |
143 | + |
144 | def map_machines(self): |
145 | """Map machines method to be implemented. |
146 | |
147 | @@ -158,6 +173,17 @@ class BaseFile: |
148 | """ |
149 | raise NotImplementedError(f"{self.__class__.__name__} missing: sorted_machines") |
150 | |
151 | + def filter_lxd_on_machine(self, machine: str): |
152 | + """Lxd containers on a machine. |
153 | + |
154 | + :param machine: machine id. |
155 | + :type machine: str |
156 | + :raises NotImplementedError: Raise if not implemented on child classes. |
157 | + """ |
158 | + raise NotImplementedError( |
159 | + f"{self.__class__.__name__} missing: filter_lxd_on_machine" |
160 | + ) |
161 | + |
162 | |
163 | @dataclass |
164 | class JujuStatusFile(BaseFile): |
165 | @@ -176,9 +202,12 @@ class JujuStatusFile(BaseFile): |
166 | for unit in units: |
167 | machine = units[unit].get("machine") |
168 | self.apps_to_machines[app].add(machine) |
169 | + self.machines_to_apps[machine].add(app) |
170 | subordinates = units[unit].get("subordinates", {}) |
171 | for sub in subordinates: |
172 | - self.apps_to_machines[sub.split("/")[0]].add(machine) |
173 | + sub_name = sub.split("/")[0] |
174 | + self.apps_to_machines[sub_name].add(machine) |
175 | + self.machines_to_apps[machine].add(sub_name) |
176 | |
177 | @staticmethod |
178 | def sorted_machines(machine: str) -> Tuple[int, str, int]: |
179 | @@ -218,6 +247,20 @@ class JujuStatusFile(BaseFile): |
180 | apps_related.update(relations.get(endpoint, [])) |
181 | return apps_related |
182 | |
183 | + def filter_lxd_on_machine(self, machine: str) -> Set: |
184 | + """Lxd containers on a machine. |
185 | + |
186 | + :param machine: machine id. |
187 | + :type machine: str |
188 | + :return: lxd containers in the machine. |
189 | + :rtype: Set |
190 | + """ |
191 | + return { |
192 | + lxd_machine |
193 | + for lxd_machine in self.machines |
194 | + if "lxd" in lxd_machine and lxd_machine.split("/")[0] == machine |
195 | + } |
196 | + |
197 | |
198 | @dataclass |
199 | class JujuBundleFile(BaseFile): |
200 | @@ -241,18 +284,26 @@ class JujuBundleFile(BaseFile): |
201 | for app in self.applications_data: |
202 | machines = self.applications_data[app].get("to", []) |
203 | self.apps_to_machines[app].update(machines) |
204 | + for machine in machines: |
205 | + self.machines_to_apps[machine].add(app) |
206 | # NOTE(gabrielcocenza) subordinates won't have the 'to' field because |
207 | # they are deployed thru relations. |
208 | subordinates = { |
209 | sub for sub, machines in self.apps_to_machines.items() if machines == set() |
210 | } |
211 | for relation in self.relations_data: |
212 | - app_1, endpoint_1, app_2, endpoint_2 = self.split_relation(relation) |
213 | + app_1, _, app_2, _ = self.split_relation(relation) |
214 | # update with the machines of the application that the subordinate charm relate. |
215 | if app_1 in subordinates: |
216 | - self.apps_to_machines[app_1].update(self.apps_to_machines[app_2]) |
217 | + sub_machines = self.apps_to_machines[app_2] |
218 | + self.apps_to_machines[app_1].update(sub_machines) |
219 | + for sub_machine in sub_machines: |
220 | + self.machines_to_apps[sub_machine].add(app_1) |
221 | elif app_2 in subordinates: |
222 | + sub_machines = self.apps_to_machines[app_1] |
223 | self.apps_to_machines[app_2].update(self.apps_to_machines[app_1]) |
224 | + for sub_machine in sub_machines: |
225 | + self.machines_to_apps[sub_machine].add(app_2) |
226 | |
227 | @staticmethod |
228 | def sorted_machines(machine: str) -> Tuple[int, str]: |
229 | @@ -303,6 +354,20 @@ class JujuBundleFile(BaseFile): |
230 | apps_related.add(app_1_ep_1.split(":")[0]) |
231 | return apps_related |
232 | |
233 | + def filter_lxd_on_machine(self, machine: str) -> Set: |
234 | + """Lxd containers on a machine. |
235 | + |
236 | + :param machine: machine id. |
237 | + :type machine: str |
238 | + :return: lxd containers in the machine. |
239 | + :rtype: Set |
240 | + """ |
241 | + return { |
242 | + lxd_machine |
243 | + for lxd_machine in self.machines |
244 | + if "lxd" in lxd_machine and lxd_machine.split(":")[1] == machine |
245 | + } |
246 | + |
247 | |
248 | def input_handler( |
249 | parsed_yaml: Dict[str, Any], applications_key: str |
250 | diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py |
251 | index b49a4b2..0f09bba 100644 |
252 | --- a/tests/unit/conftest.py |
253 | +++ b/tests/unit/conftest.py |
254 | @@ -242,17 +242,31 @@ def rules_files(): |
255 | |
256 | |
257 | @pytest.fixture |
258 | -def input_files(parsed_yaml_status, parsed_yaml_bundle): |
259 | +def input_files( |
260 | + parsed_yaml_status, |
261 | + parsed_yaml_bundle, |
262 | + parsed_hyper_converged_yaml_status, |
263 | + parsed_hyper_converged_yaml_bundle, |
264 | +): |
265 | return { |
266 | "juju-status": JujuStatusFile( |
267 | applications_data=parsed_yaml_status["applications"], |
268 | machines_data=parsed_yaml_status["machines"], |
269 | ), |
270 | + "juju-status-hyper-converged": JujuStatusFile( |
271 | + applications_data=parsed_hyper_converged_yaml_status["applications"], |
272 | + machines_data=parsed_hyper_converged_yaml_status["machines"], |
273 | + ), |
274 | "juju-bundle": JujuBundleFile( |
275 | applications_data=parsed_yaml_bundle["applications"], |
276 | machines_data=parsed_yaml_bundle["machines"], |
277 | relations_data=parsed_yaml_bundle["relations"], |
278 | ), |
279 | + "juju-bundle-parsed-hyper-converged": JujuBundleFile( |
280 | + applications_data=parsed_hyper_converged_yaml_bundle["applications"], |
281 | + machines_data=parsed_hyper_converged_yaml_bundle["machines"], |
282 | + relations_data=parsed_hyper_converged_yaml_bundle["relations"], |
283 | + ), |
284 | } |
285 | |
286 | |
287 | @@ -476,3 +490,325 @@ def parsed_yaml_bundle(): |
288 | ], |
289 | ], |
290 | } |
291 | + |
292 | + |
293 | +@pytest.fixture |
294 | +def parsed_hyper_converged_yaml_status(): |
295 | + """Representation of a hyper converged model with masakari.""" |
296 | + return { |
297 | + "machines": { |
298 | + "0": { |
299 | + "juju-status": {"current": "started"}, |
300 | + "machine-status": {"current": "running"}, |
301 | + "modification-status": { |
302 | + "current": "idle", |
303 | + }, |
304 | + "containers": { |
305 | + "0/lxd/0": { |
306 | + "juju-status": {"current": "started"}, |
307 | + "machine-status": {"current": "running"}, |
308 | + "modification-status": {"current": "applied"}, |
309 | + "constraints": "arch=amd64 spaces=", |
310 | + "hardware": "availability-zone=nova", |
311 | + }, |
312 | + "0/lxd/1": { |
313 | + "juju-status": {"current": "started"}, |
314 | + "machine-status": {"current": "running"}, |
315 | + "modification-status": {"current": "applied"}, |
316 | + "constraints": "arch=amd64 spaces=", |
317 | + "hardware": "availability-zone=nova", |
318 | + }, |
319 | + }, |
320 | + "constraints": "arch=amd64 mem=4096M", |
321 | + "hardware": "arch=amd64 cores=2 mem=4096M root-disk=40960M availability-zone=nova", |
322 | + }, |
323 | + "1": { |
324 | + "juju-status": {"current": "started"}, |
325 | + "machine-status": {"current": "running"}, |
326 | + "modification-status": {"current": "idle"}, |
327 | + "constraints": "arch=amd64 mem=4096M", |
328 | + "hardware": "arch=amd64 cores=2 mem=4096M root-disk=40960M availability-zone=nova", |
329 | + }, |
330 | + "2": { |
331 | + "juju-status": {"current": "started"}, |
332 | + "machine-status": {"current": "running"}, |
333 | + "modification-status": {"current": "idle"}, |
334 | + "constraints": "arch=amd64 mem=4096M", |
335 | + "hardware": "arch=amd64 cores=2 mem=4096M root-disk=40960M availability-zone=nova", |
336 | + }, |
337 | + "3": { |
338 | + "juju-status": {"current": "started"}, |
339 | + "machine-status": {"current": "running"}, |
340 | + "modification-status": {"current": "idle"}, |
341 | + "constraints": "arch=amd64", |
342 | + "hardware": "arch=amd64 cores=1 mem=2048M root-disk=20480M availability-zone=nova", |
343 | + }, |
344 | + }, |
345 | + "applications": { |
346 | + "ceilometer": { |
347 | + "charm": "ceilometer", |
348 | + "charm-name": "ceilometer", |
349 | + "application-status": {"current": "active"}, |
350 | + "relations": {"cluster": ["ceilometer"]}, |
351 | + "units": { |
352 | + "ceilometer/0": { |
353 | + "workload-status": {"current": "active"}, |
354 | + "juju-status": {"current": "idle"}, |
355 | + "machine": "0/lxd/0", |
356 | + } |
357 | + }, |
358 | + "endpoint-bindings": { |
359 | + "": "alpha", |
360 | + "admin": "alpha", |
361 | + "amqp": "alpha", |
362 | + "amqp-listener": "alpha", |
363 | + "ceilometer-service": "alpha", |
364 | + "certificates": "alpha", |
365 | + "cluster": "alpha", |
366 | + "event-service": "alpha", |
367 | + "ha": "alpha", |
368 | + "identity-credentials": "alpha", |
369 | + "identity-notifications": "alpha", |
370 | + "identity-service": "alpha", |
371 | + "internal": "alpha", |
372 | + "metric-service": "alpha", |
373 | + "nrpe-external-master": "alpha", |
374 | + "public": "alpha", |
375 | + "shared-db": "alpha", |
376 | + }, |
377 | + }, |
378 | + "ceph-mon": { |
379 | + "charm": "ceph-mon", |
380 | + "charm-name": "ceph-mon", |
381 | + "application-status": {"current": "active"}, |
382 | + "relations": { |
383 | + "client": ["nova-compute"], |
384 | + "mon": ["ceph-mon"], |
385 | + "osd": ["ceph-osd"], |
386 | + }, |
387 | + "units": { |
388 | + "ceph-mon/0": { |
389 | + "workload-status": {"current": "idle"}, |
390 | + "juju-status": {"current": "idle"}, |
391 | + "machine": "0", |
392 | + }, |
393 | + "ceph-mon/1": { |
394 | + "workload-status": {"current": "idle"}, |
395 | + "juju-status": {"current": "idle"}, |
396 | + "machine": "1", |
397 | + }, |
398 | + "ceph-mon/2": { |
399 | + "workload-status": {"current": "idle"}, |
400 | + "juju-status": {"current": "idle"}, |
401 | + "machine": "2", |
402 | + }, |
403 | + }, |
404 | + "endpoint-bindings": { |
405 | + "": "alpha", |
406 | + "admin": "alpha", |
407 | + "bootstrap-source": "alpha", |
408 | + "client": "alpha", |
409 | + "cluster": "alpha", |
410 | + "dashboard": "alpha", |
411 | + "mds": "alpha", |
412 | + "mon": "alpha", |
413 | + "nrpe-external-master": "alpha", |
414 | + "osd": "alpha", |
415 | + "prometheus": "alpha", |
416 | + "public": "alpha", |
417 | + "radosgw": "alpha", |
418 | + "rbd-mirror": "alpha", |
419 | + }, |
420 | + }, |
421 | + "ceph-osd": { |
422 | + "charm": "ceph-osd", |
423 | + "charm-name": "ceph-osd", |
424 | + "application-status": {"current": "active"}, |
425 | + "relations": {"mon": ["ceph-mon"]}, |
426 | + "units": { |
427 | + "ceph-osd/0": { |
428 | + "workload-status": {"current": "idle"}, |
429 | + "juju-status": {"current": "idle"}, |
430 | + "machine": "0", |
431 | + }, |
432 | + "ceph-osd/1": { |
433 | + "workload-status": {"current": "idle"}, |
434 | + "juju-status": {"current": "idle"}, |
435 | + "machine": "1", |
436 | + }, |
437 | + "ceph-osd/2": { |
438 | + "workload-status": {"current": "idle"}, |
439 | + "juju-status": {"current": "idle"}, |
440 | + "machine": "2", |
441 | + }, |
442 | + }, |
443 | + "endpoint-bindings": { |
444 | + "": "alpha", |
445 | + "cluster": "alpha", |
446 | + "mon": "alpha", |
447 | + "nrpe-external-master": "alpha", |
448 | + "public": "alpha", |
449 | + "secrets-storage": "alpha", |
450 | + }, |
451 | + }, |
452 | + "heat": { |
453 | + "charm": "heat", |
454 | + "series": "focal", |
455 | + "charm-name": "heat", |
456 | + "application-status": {"current": "active"}, |
457 | + "relations": {"cluster": ["heat"]}, |
458 | + "units": { |
459 | + "heat/0": { |
460 | + "workload-status": {"current": "idle"}, |
461 | + "juju-status": {"current": "idle"}, |
462 | + "machine": "0/lxd/1", |
463 | + } |
464 | + }, |
465 | + "endpoint-bindings": { |
466 | + "": "alpha", |
467 | + "admin": "alpha", |
468 | + "amqp": "alpha", |
469 | + "certificates": "alpha", |
470 | + "cluster": "alpha", |
471 | + "ha": "alpha", |
472 | + "heat-plugin-subordinate": "alpha", |
473 | + "identity-service": "alpha", |
474 | + "internal": "alpha", |
475 | + "nrpe-external-master": "alpha", |
476 | + "public": "alpha", |
477 | + "shared-db": "alpha", |
478 | + }, |
479 | + }, |
480 | + "masakari": { |
481 | + "charm": "masakari", |
482 | + "charm-name": "masakari", |
483 | + "application-status": {"current": "active"}, |
484 | + "relations": {"cluster": ["masakari"]}, |
485 | + "units": { |
486 | + "masakari/0": { |
487 | + "workload-status": {"current": "idle"}, |
488 | + "juju-status": {"current": "idle"}, |
489 | + "machine": "3", |
490 | + } |
491 | + }, |
492 | + "endpoint-bindings": { |
493 | + "": "alpha", |
494 | + "admin": "alpha", |
495 | + "amqp": "alpha", |
496 | + "certificates": "alpha", |
497 | + "cluster": "alpha", |
498 | + "ha": "alpha", |
499 | + "identity-service": "alpha", |
500 | + "internal": "alpha", |
501 | + "public": "alpha", |
502 | + "shared-db": "alpha", |
503 | + }, |
504 | + }, |
505 | + "nova-compute": { |
506 | + "charm": "nova-compute", |
507 | + "charm-name": "nova-compute", |
508 | + "application-status": {"current": "active"}, |
509 | + "relations": {"ceph": ["ceph-mon"], "compute-peer": ["nova-compute"]}, |
510 | + "units": { |
511 | + "nova-compute/0": { |
512 | + "workload-status": {"current": "idle"}, |
513 | + "juju-status": {"current": "idle"}, |
514 | + "machine": "0", |
515 | + }, |
516 | + "nova-compute/1": { |
517 | + "workload-status": {"current": "idle"}, |
518 | + "juju-status": {"current": "idle"}, |
519 | + "machine": "1", |
520 | + }, |
521 | + "nova-compute/2": { |
522 | + "workload-status": {"current": "idle"}, |
523 | + "juju-status": {"current": "idle"}, |
524 | + "machine": "2", |
525 | + }, |
526 | + }, |
527 | + "endpoint-bindings": { |
528 | + "": "alpha", |
529 | + "amqp": "alpha", |
530 | + "ceph": "alpha", |
531 | + "ceph-access": "alpha", |
532 | + "cloud-compute": "alpha", |
533 | + "cloud-credentials": "alpha", |
534 | + "compute-peer": "alpha", |
535 | + "ephemeral-backend": "alpha", |
536 | + "image-service": "alpha", |
537 | + "internal": "alpha", |
538 | + "ironic-api": "alpha", |
539 | + "lxd": "alpha", |
540 | + "migration": "alpha", |
541 | + "neutron-plugin": "alpha", |
542 | + "nova-ceilometer": "alpha", |
543 | + "nrpe-external-master": "alpha", |
544 | + "secrets-storage": "alpha", |
545 | + }, |
546 | + }, |
547 | + }, |
548 | + } |
549 | + |
550 | + |
551 | +@pytest.fixture |
552 | +def parsed_hyper_converged_yaml_bundle(): |
553 | + """Representation of a hyper converged model with masakari.""" |
554 | + return { |
555 | + "series": "focal", |
556 | + "applications": { |
557 | + "ceilometer": { |
558 | + "charm": "ceilometer", |
559 | + "num_units": 1, |
560 | + "to": ["lxd:0"], |
561 | + "constraints": "arch=amd64", |
562 | + }, |
563 | + "ceph-mon": { |
564 | + "charm": "ceph-mon", |
565 | + "num_units": 3, |
566 | + "to": ["0", "1", "2"], |
567 | + "constraints": "arch=amd64", |
568 | + }, |
569 | + "ceph-osd": { |
570 | + "charm": "ceph-osd", |
571 | + "num_units": 3, |
572 | + "to": ["0", "1", "2"], |
573 | + "constraints": "arch=amd64 mem=4096", |
574 | + "storage": { |
575 | + "bluestore-db": "loop,0,1024", |
576 | + "bluestore-wal": "loop,0,1024", |
577 | + "osd-devices": "loop,0,1024", |
578 | + "osd-journals": "loop,0,1024", |
579 | + }, |
580 | + }, |
581 | + "heat": { |
582 | + "charm": "heat", |
583 | + "resources": {"policyd-override": 0}, |
584 | + "num_units": 1, |
585 | + "to": ["lxd:0"], |
586 | + "constraints": "arch=amd64", |
587 | + }, |
588 | + "masakari": { |
589 | + "charm": "masakari", |
590 | + "num_units": 1, |
591 | + "to": ["3"], |
592 | + "constraints": "arch=amd64", |
593 | + }, |
594 | + "nova-compute": { |
595 | + "charm": "nova-compute", |
596 | + "num_units": 3, |
597 | + "to": ["0", "1", "2"], |
598 | + "constraints": "arch=amd64", |
599 | + "storage": {"ephemeral-device": "loop,0,10240"}, |
600 | + }, |
601 | + }, |
602 | + "machines": { |
603 | + "0": {"constraints": "arch=amd64 mem=4096"}, |
604 | + "1": {"constraints": "arch=amd64 mem=4096"}, |
605 | + "2": {"constraints": "arch=amd64 mem=4096"}, |
606 | + "3": {"constraints": "arch=amd64"}, |
607 | + }, |
608 | + "relations": [ |
609 | + ["ceph-mon:client", "nova-compute:ceph"], |
610 | + ["ceph-mon:osd", "ceph-osd:mon"], |
611 | + ], |
612 | + } |
613 | diff --git a/tests/unit/test_hyper_converged.py b/tests/unit/test_hyper_converged.py |
614 | new file mode 100644 |
615 | index 0000000..751af7a |
616 | --- /dev/null |
617 | +++ b/tests/unit/test_hyper_converged.py |
618 | @@ -0,0 +1,32 @@ |
619 | +from collections import defaultdict |
620 | + |
621 | +import pytest |
622 | + |
623 | +from jujulint.checks import hyper_converged |
624 | + |
625 | + |
626 | +@pytest.mark.parametrize( |
627 | + "masakari, input_file_type", |
628 | + [ |
629 | + (True, "juju-status-hyper-converged"), |
630 | + (False, "juju-status-hyper-converged"), |
631 | + (True, "juju-bundle-parsed-hyper-converged"), |
632 | + (False, "juju-bundle-parsed-hyper-converged"), |
633 | + ], |
634 | +) |
635 | +def test_check_hyper_converged(input_files, masakari, input_file_type): |
636 | + """Test hyper_converged models.""" |
637 | + input_file = input_files[input_file_type] |
638 | + expected_result = defaultdict(lambda: defaultdict(set)) |
639 | + if masakari and "juju-status" in input_file_type: |
640 | + expected_result["0"]["0/lxd/0"] = {"ceilometer"} |
641 | + expected_result["0"]["0/lxd/1"] = {"heat"} |
642 | + elif masakari and "juju-bundle" in input_file_type: |
643 | + expected_result["0"]["lxd:0"] = {"ceilometer", "heat"} |
644 | + else: |
645 | + # remove masakari from input file |
646 | + del input_file.applications_data["masakari"] |
647 | + del input_file.machines_data["3"] |
648 | + input_file.charms = set() |
649 | + input_file.map_file() |
650 | + assert hyper_converged.check_hyper_converged(input_file) == expected_result |
651 | diff --git a/tests/unit/test_input.py b/tests/unit/test_input.py |
652 | index 682b664..f06ff4b 100644 |
653 | --- a/tests/unit/test_input.py |
654 | +++ b/tests/unit/test_input.py |
655 | @@ -51,6 +51,18 @@ def test_file_inputs(input_files, input_file_type): |
656 | "keystone": {"lxd:1"}, |
657 | }, |
658 | }, |
659 | + "machines_to_apps": { |
660 | + "juju-status": { |
661 | + "0": {"nrpe-host", "elasticsearch"}, |
662 | + "1": {"ubuntu", "nrpe-host"}, |
663 | + "1/lxd/0": {"nrpe-container", "keystone"}, |
664 | + }, |
665 | + "juju-bundle": { |
666 | + "0": {"nrpe-host", "elasticsearch"}, |
667 | + "1": {"ubuntu", "nrpe-host"}, |
668 | + "lxd:1": {"nrpe-container", "keystone"}, |
669 | + }, |
670 | + }, |
671 | } |
672 | assert input_file.applications == expected_output["applications"] |
673 | assert input_file.machines == expected_output["machines"][input_file_type] |
674 | @@ -58,6 +70,10 @@ def test_file_inputs(input_files, input_file_type): |
675 | input_file.apps_to_machines |
676 | == expected_output["apps_to_machines"][input_file_type] |
677 | ) |
678 | + assert ( |
679 | + input_file.machines_to_apps |
680 | + == expected_output["machines_to_apps"][input_file_type] |
681 | + ) |
682 | assert input_file.charms == expected_output["charms"] |
683 | assert input_file.app_to_charm == expected_output["app_to_charm"] |
684 | assert input_file.charm_to_app == expected_output["charm_to_app"] |
685 | @@ -207,6 +223,48 @@ def test_input_handler(parsed_yaml, expected_output, request): |
686 | ) |
687 | |
688 | |
689 | +@pytest.mark.parametrize("input_file_type", ["juju-status", "juju-bundle"]) |
690 | +def test_filter_machines_by_charm(input_files, input_file_type): |
691 | + """Test filter_machines_by_charm method.""" |
692 | + input_file = input_files[input_file_type] |
693 | + if input_file_type == "juju-status": |
694 | + expected_output = { |
695 | + "nrpe": {"0", "1", "1/lxd/0"}, |
696 | + "keystone": {"1/lxd/0"}, |
697 | + "ubuntu": {"1"}, |
698 | + "elasticsearch": {"0"}, |
699 | + } |
700 | + else: |
701 | + expected_output = { |
702 | + "nrpe": {"0", "1", "lxd:1"}, |
703 | + "keystone": {"lxd:1"}, |
704 | + "ubuntu": {"1"}, |
705 | + "elasticsearch": {"0"}, |
706 | + } |
707 | + for charm in input_file.charms: |
708 | + assert input_file.filter_machines_by_charm(charm) == expected_output[charm] |
709 | + |
710 | + |
711 | +@pytest.mark.parametrize("input_file_type", ["juju-status", "juju-bundle"]) |
712 | +def test_filter_lxd_on_machine(input_files, input_file_type): |
713 | + """Test filter_lxd_on_machine method.""" |
714 | + input_file = input_files[input_file_type] |
715 | + if input_file_type == "juju-status": |
716 | + expected_output = { |
717 | + "0": set(), |
718 | + "1": {"1/lxd/0"}, |
719 | + "1/lxd/0": set(), |
720 | + } |
721 | + else: |
722 | + expected_output = { |
723 | + "0": set(), |
724 | + "1": {"lxd:1"}, |
725 | + "lxd:1": set(), |
726 | + } |
727 | + for machine in input_file.machines: |
728 | + assert input_file.filter_lxd_on_machine(machine) == expected_output[machine] |
729 | + |
730 | + |
731 | def test_raise_not_implemented_methods(parsed_yaml_status): |
732 | # declare a new input class |
733 | @dataclass |
734 | @@ -231,3 +289,6 @@ def test_raise_not_implemented_methods(parsed_yaml_status): |
735 | |
736 | with pytest.raises(NotImplementedError): |
737 | new_input.sorted_machines("0") |
738 | + |
739 | + with pytest.raises(NotImplementedError): |
740 | + new_input.filter_lxd_on_machine("0") |
741 | diff --git a/tests/unit/test_jujulint.py b/tests/unit/test_jujulint.py |
742 | index 77dbb5e..c87722d 100644 |
743 | --- a/tests/unit/test_jujulint.py |
744 | +++ b/tests/unit/test_jujulint.py |
745 | @@ -7,7 +7,8 @@ from unittest import mock |
746 | import pytest |
747 | import yaml |
748 | |
749 | -from jujulint import check_spaces, lint, relations |
750 | +from jujulint import lint |
751 | +from jujulint.checks import relations, spaces |
752 | from jujulint.lint import VALID_LOG_LEVEL |
753 | |
754 | |
755 | @@ -1424,7 +1425,7 @@ applications: |
756 | This warning should be triggerred if some applications have bindings and some |
757 | dont. |
758 | """ |
759 | - logger_mock = mocker.patch.object(check_spaces, "LOGGER") |
760 | + logger_mock = mocker.patch.object(spaces, "LOGGER") |
761 | |
762 | app_without_binding = "prometheus-app" |
763 | bundle = { |
764 | @@ -1458,7 +1459,7 @@ applications: |
765 | mentioned explicitly will be bound to this default space. |
766 | Juju lint should raise warning if bundles do not define default space. |
767 | """ |
768 | - logger_mock = mocker.patch.object(check_spaces, "LOGGER") |
769 | + logger_mock = mocker.patch.object(spaces, "LOGGER") |
770 | app_without_default_space = "telegraf-app" |
771 | |
772 | bundle = { |
773 | @@ -1490,7 +1491,7 @@ applications: |
774 | |
775 | def test_check_spaces_multi_model_warning(self, linter, mocker): |
776 | """Test that check_spaces shows warning if some application are from another model.""" |
777 | - logger_mock = mocker.patch.object(check_spaces, "LOGGER") |
778 | + logger_mock = mocker.patch.object(spaces, "LOGGER") |
779 | |
780 | app_another_model = "prometheus-app" |
781 | bundle = { |
782 | @@ -1745,3 +1746,60 @@ applications: |
783 | logger_mock.assert_has_calls( |
784 | [mocker.call(expected_message, level=logging.ERROR)] |
785 | ) |
786 | + |
787 | + @pytest.mark.parametrize( |
788 | + "input_file_type", |
789 | + ["juju-status-hyper-converged", "juju-bundle-parsed-hyper-converged"], |
790 | + ) |
791 | + def test_check_hyper_converged(self, linter, input_files, mocker, input_file_type): |
792 | + """Test check_hyper_converged.""" |
793 | + input_file = input_files[input_file_type] |
794 | + mock_message_handler = mocker.patch("jujulint.lint.Linter.message_handler") |
795 | + msg = ( |
796 | + "Deployment has Masakari and the machine: '{}' " |
797 | + "has nova/osd and the lxd: '{}' with those services {}" |
798 | + ) |
799 | + expected_output = [ |
800 | + mocker.call( |
801 | + { |
802 | + "id": "hyper-converged-masakari", |
803 | + "tags": ["hyper-converged", "masakari"], |
804 | + "message": msg.format( |
805 | + "0", |
806 | + "lxd:0", |
807 | + ["ceilometer", "heat"], |
808 | + ), |
809 | + }, |
810 | + log_level=logging.WARNING, |
811 | + ) |
812 | + ] |
813 | + if "juju-status" in input_file_type: |
814 | + expected_output = [ |
815 | + mocker.call( |
816 | + { |
817 | + "id": "hyper-converged-masakari", |
818 | + "tags": ["hyper-converged", "masakari"], |
819 | + "message": msg.format( |
820 | + "0", |
821 | + "0/lxd/0", |
822 | + ["ceilometer"], |
823 | + ), |
824 | + }, |
825 | + log_level=logging.WARNING, |
826 | + ), |
827 | + mocker.call( |
828 | + { |
829 | + "id": "hyper-converged-masakari", |
830 | + "tags": ["hyper-converged", "masakari"], |
831 | + "message": msg.format( |
832 | + "0", |
833 | + "0/lxd/1", |
834 | + ["heat"], |
835 | + ), |
836 | + }, |
837 | + log_level=logging.WARNING, |
838 | + ), |
839 | + ] |
840 | + |
841 | + linter.check_hyper_converged(input_file) |
842 | + mock_message_handler.assert_has_calls(expected_output, any_order=True) |
843 | diff --git a/tests/unit/test_relations.py b/tests/unit/test_relations.py |
844 | index f13d544..121fe9e 100644 |
845 | --- a/tests/unit/test_relations.py |
846 | +++ b/tests/unit/test_relations.py |
847 | @@ -2,7 +2,7 @@ |
848 | """Test the relations module.""" |
849 | import pytest |
850 | |
851 | -from jujulint import relations |
852 | +from jujulint.checks import relations |
853 | |
854 | CHARM_TO_APP = {"nrpe-host", "nrpe-container"} |
855 | CHARM = "nrpe" |
856 | @@ -352,7 +352,7 @@ def test_relations_raise_not_implemented(input_files, mocker): |
857 | """Ensure that a new class that not implement mandatory methods raises error.""" |
858 | logger_mock = mocker.patch.object(relations, "LOGGER") |
859 | mocker.patch( |
860 | - "jujulint.relations.RelationRule.relation_exist_check", |
861 | + "jujulint.checks.relations.RelationRule.relation_exist_check", |
862 | side_effect=NotImplementedError(), |
863 | ) |
864 | input_file = input_files["juju-status"] |
865 | diff --git a/tests/unit/test_check_spaces.py b/tests/unit/test_spaces.py |
866 | similarity index 80% |
867 | rename from tests/unit/test_check_spaces.py |
868 | rename to tests/unit/test_spaces.py |
869 | index 9b77e66..db16505 100644 |
870 | --- a/tests/unit/test_check_spaces.py |
871 | +++ b/tests/unit/test_spaces.py |
872 | @@ -1,9 +1,9 @@ |
873 | -"""Tests for check_spaces.py module.""" |
874 | +"""Tests for spaces.py module.""" |
875 | from unittest.mock import call |
876 | |
877 | import pytest |
878 | |
879 | -from jujulint import check_spaces |
880 | +from jujulint.checks import spaces |
881 | |
882 | |
883 | def test_relation_init(): |
884 | @@ -11,7 +11,7 @@ def test_relation_init(): |
885 | ep_1 = "Endpoint 1" |
886 | ep_2 = "Endpoint 2" |
887 | |
888 | - relation = check_spaces.Relation(ep_1, ep_2) |
889 | + relation = spaces.Relation(ep_1, ep_2) |
890 | |
891 | assert relation.endpoint1 == ep_1 |
892 | assert relation.endpoint2 == ep_2 |
893 | @@ -23,7 +23,7 @@ def test_relation_str(): |
894 | ep_2 = "Endpoint 2" |
895 | expected_str = "Relation({} - {})".format(ep_1, ep_2) |
896 | |
897 | - relation = check_spaces.Relation(ep_1, ep_2) |
898 | + relation = spaces.Relation(ep_1, ep_2) |
899 | |
900 | assert str(relation) == expected_str |
901 | |
902 | @@ -63,8 +63,8 @@ def test_relation_str(): |
903 | ) |
904 | def test_relation_eq(rel_1_ep_1, rel_1_ep_2, rel_2_ep_1, rel_2_ep_2, expected_result): |
905 | """Test equality operator of Relation class. Only return true if both endpoints match.""" |
906 | - relation_1 = check_spaces.Relation(rel_1_ep_1, rel_1_ep_2) |
907 | - relation_2 = check_spaces.Relation(rel_2_ep_1, rel_2_ep_2) |
908 | + relation_1 = spaces.Relation(rel_1_ep_1, rel_1_ep_2) |
909 | + relation_2 = spaces.Relation(rel_2_ep_1, rel_2_ep_2) |
910 | |
911 | assert (relation_1 == relation_2) == expected_result |
912 | |
913 | @@ -74,7 +74,7 @@ def test_relation_endpoints_prop(): |
914 | ep_1 = "Endpoint 1" |
915 | ep_2 = "Endpoint 2" |
916 | |
917 | - relation = check_spaces.Relation(ep_1, ep_2) |
918 | + relation = spaces.Relation(ep_1, ep_2) |
919 | |
920 | assert relation.endpoints == [ep_1, ep_2] |
921 | |
922 | @@ -105,7 +105,7 @@ def test_space_mismatch_init(input_order, output_order): |
923 | This test also verifies that spaces in SpaceMismatch instance are ordered |
924 | alphabetically based on the endpoint name. |
925 | """ |
926 | - mismatch_instance = check_spaces.SpaceMismatch(*input_order) |
927 | + mismatch_instance = spaces.SpaceMismatch(*input_order) |
928 | |
929 | # Assert that endpoints are alphabetically reordered |
930 | assert mismatch_instance.endpoint1 == output_order[0] |
931 | @@ -124,7 +124,7 @@ def test_space_mismatch_str(): |
932 | ep_1, space_1, ep_2, space_2 |
933 | ) |
934 | |
935 | - mismatch_instance = check_spaces.SpaceMismatch(ep_1, space_1, ep_2, space_2) |
936 | + mismatch_instance = spaces.SpaceMismatch(ep_1, space_1, ep_2, space_2) |
937 | |
938 | assert str(mismatch_instance) == expected_str |
939 | |
940 | @@ -136,9 +136,9 @@ def test_space_mismatch_relation_prop(): |
941 | space_1 = "Space 1" |
942 | space_2 = "Space 2" |
943 | |
944 | - expected_relation = check_spaces.Relation(ep_1, ep_2) |
945 | + expected_relation = spaces.Relation(ep_1, ep_2) |
946 | |
947 | - mismatch_instance = check_spaces.SpaceMismatch(ep_1, space_1, ep_2, space_2) |
948 | + mismatch_instance = spaces.SpaceMismatch(ep_1, space_1, ep_2, space_2) |
949 | |
950 | assert mismatch_instance.relation == expected_relation |
951 | |
952 | @@ -156,9 +156,9 @@ def test_space_mismatch_get_charm_relation(): |
953 | |
954 | app_map = {app_1: charm_1, app_2: charm_2} |
955 | |
956 | - expected_relation = check_spaces.Relation("ubuntu:endpoint_1", "nrpe:endpoint_2") |
957 | + expected_relation = spaces.Relation("ubuntu:endpoint_1", "nrpe:endpoint_2") |
958 | |
959 | - mismatch_instance = check_spaces.SpaceMismatch(ep_1, space_1, ep_2, space_2) |
960 | + mismatch_instance = spaces.SpaceMismatch(ep_1, space_1, ep_2, space_2) |
961 | |
962 | assert mismatch_instance.get_charm_relation(app_map) == expected_relation |
963 | |
964 | @@ -173,32 +173,28 @@ def test_find_space_mismatches(use_cmr, mocker): |
965 | space_2 = "space 2" |
966 | app_endpoint_1 = app_1 + ":endpoint" |
967 | app_endpoint_2 = app_2 + ":endpoint" |
968 | - relation = check_spaces.Relation( |
969 | - app_endpoint_1, "XModel" if use_cmr else app_endpoint_2 |
970 | - ) |
971 | + relation = spaces.Relation(app_endpoint_1, "XModel" if use_cmr else app_endpoint_2) |
972 | app_list = [app_1, app_2] |
973 | app_spaces = {app_1: {space_1: "foo"}, app_2: {space_2: "bar"}} |
974 | |
975 | app_list_mock = mocker.patch.object( |
976 | - check_spaces, "get_juju_applications", return_value=app_list |
977 | + spaces, "get_juju_applications", return_value=app_list |
978 | ) |
979 | app_spaces_mock = mocker.patch.object( |
980 | - check_spaces, "get_application_spaces", return_value=app_spaces |
981 | + spaces, "get_application_spaces", return_value=app_spaces |
982 | ) |
983 | rel_list_mock = mocker.patch.object( |
984 | - check_spaces, "get_application_relations", return_value=[relation] |
985 | + spaces, "get_application_relations", return_value=[relation] |
986 | ) |
987 | rel_space_mock = mocker.patch.object( |
988 | - check_spaces, "get_relation_space", side_effect=[space_1, space_2] |
989 | + spaces, "get_relation_space", side_effect=[space_1, space_2] |
990 | ) |
991 | |
992 | expected_mismatch = [ |
993 | - check_spaces.SpaceMismatch( |
994 | - relation.endpoint1, space_1, relation.endpoint2, space_2 |
995 | - ) |
996 | + spaces.SpaceMismatch(relation.endpoint1, space_1, relation.endpoint2, space_2) |
997 | ] |
998 | |
999 | - mismatch = check_spaces.find_space_mismatches(sample_yaml, True) |
1000 | + mismatch = spaces.find_space_mismatches(sample_yaml, True) |
1001 | result_pairs = zip(expected_mismatch, mismatch) |
1002 | |
1003 | app_list_mock.assert_called_once_with(sample_yaml) |
1004 | @@ -224,7 +220,7 @@ def test_get_juju_applications(): |
1005 | |
1006 | expected_apps = [app_1, app_2] |
1007 | |
1008 | - apps = check_spaces.get_juju_applications(sample_yaml) |
1009 | + apps = spaces.get_juju_applications(sample_yaml) |
1010 | |
1011 | assert apps == expected_apps |
1012 | |
1013 | @@ -235,7 +231,7 @@ def test_get_application_spaces(mocker): |
1014 | This test also verifies that default binding to space "alpha" is added to applications |
1015 | that do not specify any bindings. |
1016 | """ |
1017 | - logger_mock = mocker.patch.object(check_spaces, "LOGGER") |
1018 | + logger_mock = mocker.patch.object(spaces, "LOGGER") |
1019 | default_binding = "" |
1020 | default_space = "custom_default_space" |
1021 | public_binding = "public" |
1022 | @@ -269,7 +265,7 @@ def test_get_application_spaces(mocker): |
1023 | app_list[2]: {default_binding: "alpha"}, |
1024 | } |
1025 | |
1026 | - app_spaces = check_spaces.get_application_spaces(app_list, sample_yaml) |
1027 | + app_spaces = spaces.get_application_spaces(app_list, sample_yaml) |
1028 | |
1029 | # Verify that all the bindings for properly defined app were returned |
1030 | # Verify that default binding was added to app that did not have any bindings defined |
1031 | @@ -298,11 +294,11 @@ def test_get_application_relations(): |
1032 | } |
1033 | |
1034 | expected_relations = [ |
1035 | - check_spaces.Relation("ubuntu:juju-info", "nrpe:general-info"), |
1036 | - check_spaces.Relation("vault:shared-db", "mysql-innodb-cluster:shared-db"), |
1037 | + spaces.Relation("ubuntu:juju-info", "nrpe:general-info"), |
1038 | + spaces.Relation("vault:shared-db", "mysql-innodb-cluster:shared-db"), |
1039 | ] |
1040 | |
1041 | - relations = check_spaces.get_application_relations(sample_yaml) |
1042 | + relations = spaces.get_application_relations(sample_yaml) |
1043 | |
1044 | assert relations == expected_relations |
1045 | |
1046 | @@ -323,21 +319,21 @@ def test_get_relation_space(use_explicit_binding): |
1047 | else: |
1048 | expected_space = default_space |
1049 | |
1050 | - space = check_spaces.get_relation_space(endpoint, app_spaces) |
1051 | + space = spaces.get_relation_space(endpoint, app_spaces) |
1052 | |
1053 | assert space == expected_space |
1054 | |
1055 | |
1056 | def test_get_relation_space_cmr(mocker): |
1057 | """Test getting space for cross model relation.""" |
1058 | - logger_mock = mocker.patch.object(check_spaces, "LOGGER") |
1059 | + logger_mock = mocker.patch.object(spaces, "LOGGER") |
1060 | app_name = "ubuntu" |
1061 | interface = "juju_info" |
1062 | endpoint = app_name + ":" + interface |
1063 | |
1064 | app_spaces = {} |
1065 | |
1066 | - space = check_spaces.get_relation_space(endpoint, app_spaces) |
1067 | + space = spaces.get_relation_space(endpoint, app_spaces) |
1068 | |
1069 | assert space == "XModel" |
1070 | logger_mock.warning.assert_called_once_with( |
1071 | diff --git a/tox.ini b/tox.ini |
1072 | index 4dea114..08183e2 100644 |
1073 | --- a/tox.ini |
1074 | +++ b/tox.ini |
1075 | @@ -23,7 +23,7 @@ deps = |
1076 | -r{toxinidir}/requirements.txt |
1077 | -r{toxinidir}/tests/unit/requirements.txt |
1078 | commands = |
1079 | - pytest -v \ |
1080 | + pytest -vv \ |
1081 | --cov=jujulint \ |
1082 | --new-first \ |
1083 | --last-failed \ |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.