Merge ~elmo/juju-lint:fixes into juju-lint:master

Proposed by James Troup
Status: Merged
Approved by: James Hebden
Approved revision: bc012f6b21be1573f1e9bd79af5e50696a143f25
Merged at revision: 39fd901463f08610b01945c6eebd3ab37d265ca0
Proposed branch: ~elmo/juju-lint:fixes
Merge into: juju-lint:master
Diff against target: 279 lines (+89/-49)
3 files modified
contrib/canonical-rules.yaml (+13/-1)
jujulint/lint.py (+57/-48)
jujulint/util.py (+19/-0)
Reviewer Review Type Date Requested Status
James Hebden (community) Approve
Review via email: mp+393528@code.launchpad.net

Commit message

Fix config checking, promote hw-health to mandatory + more

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
James Hebden (ec0) wrote :

Looks good to me, +1

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

Change successfully merged at revision 39fd901463f08610b01945c6eebd3ab37d265ca0

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/contrib/canonical-rules.yaml b/contrib/canonical-rules.yaml
2index 9555a15..0961671 100644
3--- a/contrib/canonical-rules.yaml
4+++ b/contrib/canonical-rules.yaml
5@@ -81,6 +81,10 @@ subordinates:
6 where: host only # You don't want NTP in a container duelling with ntp in the host
7 thruk-agent:
8 where: on nagios
9+ hw-health:
10+ where: host only
11+ logrotate:
12+ where: all
13
14 operations mandatory: &operations-mandatory-charms
15 - elasticsearch
16@@ -92,7 +96,6 @@ operations mandatory: &operations-mandatory-charms
17
18 operations optional: &operations-optional-charms
19 - infra-node
20- - hw-health
21 - cloudstats
22
23 operations openstack mandatory: &operations-openstack-mandatory-charms
24@@ -115,6 +118,8 @@ operations subordinates: &operations-mandatory-subs
25 - ntp
26 - telegraf
27 - thruk-agent
28+ - hw-health
29+ - logrotated
30
31 operations optional subordinates: &operations-optional-subs
32 - policy-routing
33@@ -196,6 +201,12 @@ masakari-charms: &masakari-charms
34 - masakari-monitors
35 - pacemaker-remote
36
37+trilio-charms: &trilio-charms
38+ - trilio-dm-api
39+ - trilio-horizon-plugin
40+ - trilio-data-mover
41+ - trilio-wlm
42+
43 openstack charms: &openstack-charms
44 - *openstack-mandatory-charms
45 - *openstack-mandatory-deps
46@@ -204,6 +215,7 @@ openstack charms: &openstack-charms
47 - *cisco-aci-charms
48 - *ovn-charms
49 - *masakari-charms
50+ - *trilio-charms
51
52 known charms:
53 - ubuntu
54diff --git a/jujulint/lint.py b/jujulint/lint.py
55index 26f2c81..70f5ae6 100755
56--- a/jujulint/lint.py
57+++ b/jujulint/lint.py
58@@ -28,7 +28,7 @@ import yaml
59 from attr import attrs, attrib
60 import attr
61
62-from jujulint.util import flatten_list, is_container
63+from jujulint.util import flatten_list, is_container, extract_charm_name
64 from jujulint.logging import Logger
65
66 # TODO:
67@@ -37,12 +37,6 @@ from jujulint.logging import Logger
68 # - info mode, e.g. num of machines, version (e.g. look at ceph), architecture
69
70
71-class InvalidCharmNameError(Exception):
72- """Represents an invalid charm name being processed."""
73-
74- pass
75-
76-
77 @attrs
78 class ModelInfo(object):
79 """Represent information obtained from juju status data."""
80@@ -177,7 +171,7 @@ class Linter:
81 current = self.atoi(config[rule])
82 expected = self.atoi(check_value)
83 if current >= expected:
84- self.logger.info(
85+ self.logger.debug(
86 "[{}] [{}/{}] (PASS) Application {} has config for {} which is >= {}: {}.".format(
87 self.cloud_name,
88 self.controller_name,
89@@ -217,7 +211,7 @@ class Linter:
90 """Check if value is set per rule constraints."""
91 if rule in config:
92 if check_value is True:
93- self.logger.info(
94+ self.logger.debug(
95 "[{}] [{}/{}] (PASS) Application {} correctly has manual config for {}: {}.".format(
96 self.cloud_name,
97 self.controller_name,
98@@ -240,7 +234,7 @@ class Linter:
99 )
100 return False
101 if check_value is False:
102- self.logger.info(
103+ self.logger.debug(
104 "[{}] [{}/{}] (PASS) Application {} is correctly using default config for {}.".format(
105 self.cloud_name, self.controller_name, self.model_name, name, rule,
106 )
107@@ -262,7 +256,7 @@ class Linter:
108 except re.error:
109 match = check_value == config[rule]
110 if match:
111- self.logger.info(
112+ self.logger.debug(
113 "[{}] [{}/{}] Application {} has correct setting for {}: Expected {}, got {}.".format(
114 self.cloud_name,
115 self.controller_name,
116@@ -285,6 +279,18 @@ class Linter:
117 config[rule],
118 )
119 )
120+ return False
121+ else:
122+ self.logger.warn(
123+ "[{}] [{}/{}] When checking if application {} has no config for {}, can't determine if == {}.".format(
124+ self.cloud_name,
125+ self.controller_name,
126+ self.model_name,
127+ name,
128+ rule,
129+ check_value,
130+ )
131+ )
132
133 def check_config(self, name, config, rules):
134 """Check application against provided rules."""
135@@ -319,28 +325,38 @@ class Linter:
136 for application in applications.keys():
137 # look for config rules for this application
138 lint_rules = []
139- if "charm-name" in applications[application]:
140- charm_name = applications[application]["charm-name"]
141- if "config" in self.lint_rules:
142- if charm_name in self.lint_rules["config"]:
143- lint_rules = self.lint_rules["config"][charm_name].items()
144-
145- if self.cloud_type == "openstack":
146- # process openstack config rules
147- if "openstack config" in self.lint_rules:
148- if charm_name in self.lint_rules["openstack config"]:
149- lint_rules.extend(
150- self.lint_rules["openstack config"][charm_name].items()
151- )
152+ if "charm" not in applications[application]:
153+ self.logger.warn(
154+ "[{}] [{}/{}] Application {} has no charm.".format(
155+ self.cloud_name,
156+ self.controller_name,
157+ self.model_name,
158+ application,
159+ )
160+ )
161+ continue
162
163- if lint_rules:
164- if "options" in applications[application]:
165- self.check_config(
166- application,
167- applications[application]["options"],
168- lint_rules,
169+ charm_name = extract_charm_name(applications[application]["charm"])
170+ if "config" in self.lint_rules:
171+ if charm_name in self.lint_rules["config"]:
172+ lint_rules = self.lint_rules["config"][charm_name].items()
173+
174+ if self.cloud_type == "openstack":
175+ # process openstack config rules
176+ if "openstack config" in self.lint_rules:
177+ if charm_name in self.lint_rules["openstack config"]:
178+ lint_rules.extend(
179+ self.lint_rules["openstack config"][charm_name].items()
180 )
181
182+ if lint_rules:
183+ if "options" in applications[application]:
184+ self.check_config(
185+ application,
186+ applications[application]["options"],
187+ lint_rules,
188+ )
189+
190 def check_subs(self):
191 """Check the subordinates in the model."""
192 all_or_nothing = set()
193@@ -675,17 +691,9 @@ class Linter:
194 """Process applications in the model, validating and normalising the names."""
195 for app in applications:
196 if "charm" in applications[app]:
197- charm = applications[app]["charm"]
198- match = re.match(
199- r"^(?:\w+:)?(?:~[\w-]+/)?(?:\w+/)?([a-zA-Z0-9-]+?)(?:-\d+)?$", charm
200- )
201- if not match:
202- raise InvalidCharmNameError(
203- "charm name '{}' is invalid".format(charm)
204- )
205- charm = match.group(1)
206- self.model.charms.add(charm)
207- self.model.app_to_charm[app] = charm
208+ charm_name = extract_charm_name(applications[app]["charm"])
209+ self.model.charms.add(charm_name)
210+ self.model.app_to_charm[app] = charm_name
211 else:
212 self.logger.error(
213 "[{}] [{}/{}] Could not detect which charm is used for application {}".format(
214@@ -895,23 +903,24 @@ class Linter:
215 self.check_subs()
216 self.check_charms()
217
218- if parsed_yaml.get("machines"):
219+ if "relations" not in parsed_yaml:
220 self.map_machines_to_az(parsed_yaml["machines"])
221 self.check_azs(parsed_yaml[applications])
222 self.check_statuses(parsed_yaml, applications)
223 else:
224 self.logger.warn(
225 (
226- "[{}] [{}/{}] No machine status present in model."
227- "possibly a bundle without status, skipping AZ checks"
228+ "[{}] [{}/{}] Relations data found; assuming a bundle and "
229+ "skipping AZ and status checks."
230 ).format(
231 self.cloud_name, self.model_name, self.controller_name,
232 )
233 )
234
235 self.results()
236- self.logger.warn(
237- "[{}] [{}/{}] Model contains no applications, skipping.".format(
238- self.cloud_name, self.controller_name, self.model_name,
239+ else:
240+ self.logger.warn(
241+ "[{}] [{}/{}] Model contains no applications, skipping.".format(
242+ self.cloud_name, self.controller_name, self.model_name,
243+ )
244 )
245- )
246diff --git a/jujulint/util.py b/jujulint/util.py
247index 2b7582a..f376640 100644
248--- a/jujulint/util.py
249+++ b/jujulint/util.py
250@@ -18,6 +18,14 @@
251 # along with this program. If not, see <http://www.gnu.org/licenses/>.
252 """Utility library for all helpful functions this project uses."""
253
254+import re
255+
256+
257+class InvalidCharmNameError(Exception):
258+ """Represents an invalid charm name being processed."""
259+
260+ pass
261+
262
263 def flatten_list(lumpy_list):
264 """Flatten a list potentially containing other lists."""
265@@ -36,3 +44,14 @@ def is_container(machine):
266 return True
267 else:
268 return False
269+
270+
271+def extract_charm_name(charm):
272+ match = re.match(
273+ r"^(?:\w+:)?(?:~[\w-]+/)?(?:\w+/)?([a-zA-Z0-9-]+?)(?:-\d+)?$", charm
274+ )
275+ if not match:
276+ raise InvalidCharmNameError(
277+ "charm name '{}' is invalid".format(charm)
278+ )
279+ return match.group(1)

Subscribers

People subscribed via source and target branches