Merge ~jfguedez/juju-lint:bug/1944406 into juju-lint:master

Proposed by Jose Guedez
Status: Merged
Approved by: James Troup
Approved revision: b8b70ad9c3ee77c6bf4f533ba9c8f6924050e221
Merged at revision: ea337d4af71d1ee27c7298a43d3cb7da9b2ae502
Proposed branch: ~jfguedez/juju-lint:bug/1944406
Merge into: juju-lint:master
Diff against target: 190 lines (+102/-15)
3 files modified
contrib/includes/base.yaml (+2/-0)
jujulint/lint.py (+32/-15)
tests/test_jujulint.py (+68/-0)
Reviewer Review Type Date Requested Status
Juju Lint maintainers Pending
Review via email: mp+409033@code.launchpad.net

This proposal supersedes a proposal from 2021-09-21.

Commit message

Add a "suffix" option for config checks

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

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

Revision history for this message
James Troup (elmo) wrote : Posted in a previous version of this proposal

So here's the problem: we're trying to get rid of nrpe-container/nrpe-{host,physical}. Can I suggest instead a change to the nrpe charm which disables these checks if run in a container? See also:

  https://code.launchpad.net/~vultaire/charm-nrpe/+git/charm-nrpe/+merge/407276

Revision history for this message
Jose Guedez (jfguedez) wrote : Posted in a previous version of this proposal

Hi James,

I updated the code to be able to handle both situations (to account for a transition period for existing setups). Also added several test cases to cover the success/failure cases for the case with and without suffixes.

Please take another look when you have a chance. Thanks.

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 :

Change successfully merged at revision ea337d4af71d1ee27c7298a43d3cb7da9b2ae502

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/contrib/includes/base.yaml b/contrib/includes/base.yaml
index bc39a46..d8063eb 100644
--- a/contrib/includes/base.yaml
+++ b/contrib/includes/base.yaml
@@ -8,8 +8,10 @@ config:
8 nrpe:8 nrpe:
9 lacp_bonds:9 lacp_bonds:
10 neq: ""10 neq: ""
11 suffixes: [host, physical]
11 netlinks:12 netlinks:
12 neq: ""13 neq: ""
14 suffixes: [host, physical]
13 landscape-client:15 landscape-client:
14 disable-unattended-upgrades:16 disable-unattended-upgrades:
15 eq: true17 eq: true
diff --git a/jujulint/lint.py b/jujulint/lint.py
index 58ce1f1..6c425bd 100755
--- a/jujulint/lint.py
+++ b/jujulint/lint.py
@@ -161,7 +161,9 @@ class Linter:
161 for sub in subordinates:161 for sub in subordinates:
162 if sub in self.model.subs_on_machines[machine]:162 if sub in self.model.subs_on_machines[machine]:
163 charm = self.model.app_to_charm[sub]163 charm = self.model.app_to_charm[sub]
164 allow_multiple = self.lint_rules['subordinates'][charm].get("allow-multiple")164 allow_multiple = self.lint_rules["subordinates"][charm].get(
165 "allow-multiple"
166 )
165 if not allow_multiple:167 if not allow_multiple:
166 self.model.duelling_subs.setdefault(sub, set())168 self.model.duelling_subs.setdefault(sub, set())
167 self.model.duelling_subs[sub].add(machine)169 self.model.duelling_subs[sub].add(machine)
@@ -341,20 +343,42 @@ class Linter:
341 )343 )
342 return False344 return False
343345
344 def check_config(self, name, config, rules):346 def check_config(self, app_name, config, rules):
345 """Check application against provided rules."""347 """Check application against provided rules."""
346 rules = dict(rules)348 rules = dict(rules)
347 for rule in rules:349 for rule in rules:
348 self._log_with_header("Checking {} for configuration {}".format(name, rule))350 self._log_with_header(
351 "Checking {} for configuration {}".format(app_name, rule)
352 )
353
354 # Handle app suffix for config checks. If the suffix is provided
355 # and it does not match, then we skip the check. LP#1944406
356 # The base charm name is always checked if present.
357 suffixes = rules[rule].pop("suffixes", [])
358 if suffixes:
359 charm_name = self.model.app_to_charm[app_name]
360 target_app_names = [
361 "{}-{}".format(charm_name, suffix) for suffix in suffixes
362 ]
363 target_app_names.append(charm_name)
364
365 if app_name not in target_app_names:
366 self._log_with_header(
367 "The app name didn't match any name target for this charm: '{}' (skipping check)".format(
368 app_name
369 )
370 )
371 continue
372
349 for check_op, check_value in rules[rule].items():373 for check_op, check_value in rules[rule].items():
350 # check_op should be the operator name, e.g. (eq, neq, gte, isset)374 # check_op should be the operator name, e.g. (eq, neq, gte, isset)
351 if check_op in VALID_CONFIG_CHECKS:375 if check_op in VALID_CONFIG_CHECKS:
352 check_method = getattr(self, check_op)376 check_method = getattr(self, check_op)
353 check_method(name, check_value, rule, config)377 check_method(app_name, check_value, rule, config)
354 else:378 else:
355 self._log_with_header(379 self._log_with_header(
356 "Application {} has unknown check operation for {}: {}.".format(380 "Application {} has unknown check operation for {}: {}.".format(
357 name,381 app_name,
358 rule,382 rule,
359 check_op,383 check_op,
360 ),384 ),
@@ -429,9 +453,7 @@ class Linter:
429 self._log_with_header("... matched, not wanted on this host")453 self._log_with_header("... matched, not wanted on this host")
430 continue454 continue
431 elif where == "host only":455 elif where == "host only":
432 self._log_with_header(456 self._log_with_header("requirement is 'host only' form....")
433 "requirement is 'host only' form...."
434 )
435 if utils.is_container(machine):457 if utils.is_container(machine):
436 self._log_with_header("... and we are a container, checking")458 self._log_with_header("... and we are a container, checking")
437 # XXX check alternate names?459 # XXX check alternate names?
@@ -442,9 +464,7 @@ class Linter:
442 continue464 continue
443 self._log_with_header("... and we are a host, will fallthrough")465 self._log_with_header("... and we are a host, will fallthrough")
444 elif where == "metal only":466 elif where == "metal only":
445 self._log_with_header(467 self._log_with_header("requirement is 'metal only' form....")
446 "requirement is 'metal only' form...."
447 )
448 if not utils.is_metal(machine, machines_data.get(machine, {})):468 if not utils.is_metal(machine, machines_data.get(machine, {})):
449 self._log_with_header("... and we are not a metal, checking")469 self._log_with_header("... and we are not a metal, checking")
450 if required_sub in present_subs:470 if required_sub in present_subs:
@@ -483,10 +503,7 @@ class Linter:
483 looking_for = "{}-{}".format(required_sub, suffix)503 looking_for = "{}-{}".format(required_sub, suffix)
484 self._log_with_header("-> Looking for {}".format(looking_for))504 self._log_with_header("-> Looking for {}".format(looking_for))
485 if looking_for in present_subs:505 if looking_for in present_subs:
486 self.logger.debug("-> FOUND!!!")506 self._log_with_header("-> FOUND!!!")
487 self.cloud_name,
488 self.controller_name,
489 self.model_name,
490 found = True507 found = True
491 if not found:508 if not found:
492 for sub in present_subs:509 for sub in present_subs:
diff --git a/tests/test_jujulint.py b/tests/test_jujulint.py
index c74c550..d865c63 100644
--- a/tests/test_jujulint.py
+++ b/tests/test_jujulint.py
@@ -433,6 +433,74 @@ class TestLinter:
433 assert errors[0]["expected_value"] is False433 assert errors[0]["expected_value"] is False
434 assert errors[0]["actual_value"] is True434 assert errors[0]["actual_value"] is True
435435
436 def test_config_eq_suffix_match(self, linter, juju_status):
437 """Test the config condition 'eq'. when suffix matches."""
438 linter.lint_rules["config"] = {
439 "ubuntu": {"fake-opt": {"eq": False, "suffixes": ["host", "physical"]}}
440 }
441 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
442 juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop(
443 "ubuntu"
444 )
445 linter.do_lint(juju_status)
446
447 errors = linter.output_collector["errors"]
448 assert len(errors) == 1
449 assert errors[0]["id"] == "config-eq-check"
450 assert errors[0]["application"] == "ubuntu-host"
451 assert errors[0]["rule"] == "fake-opt"
452 assert errors[0]["expected_value"] is False
453 assert errors[0]["actual_value"] is True
454
455 def test_config_eq_suffix_match_charm_name(self, linter, juju_status):
456 """Test the config condition 'eq'. when suffix and base charm name."""
457 linter.lint_rules["config"] = {
458 "ubuntu": {"fake-opt": {"eq": False, "suffixes": ["host", "physical"]}}
459 }
460 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
461 linter.do_lint(juju_status)
462
463 errors = linter.output_collector["errors"]
464 assert len(errors) == 1
465 assert errors[0]["id"] == "config-eq-check"
466 assert errors[0]["application"] == "ubuntu"
467 assert errors[0]["rule"] == "fake-opt"
468 assert errors[0]["expected_value"] is False
469 assert errors[0]["actual_value"] is True
470
471 def test_config_eq_suffix_skip(self, linter, juju_status):
472 """Test the config condition 'eq'. when suffix doesn't match."""
473 linter.lint_rules["config"] = {
474 "ubuntu": {"fake-opt": {"eq": False, "suffixes": ["host", "physical"]}}
475 }
476 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
477 juju_status["applications"]["ubuntu-container"] = juju_status[
478 "applications"
479 ].pop("ubuntu")
480 linter.do_lint(juju_status)
481
482 errors = linter.output_collector["errors"]
483 assert not errors
484
485 def test_config_eq_no_suffix_check_all(self, linter, juju_status):
486 """Test the config condition 'eq'. when no suffix all should be checked."""
487 linter.lint_rules["config"] = {
488 "ubuntu": {"fake-opt": {"eq": False}}
489 }
490 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
491 juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop(
492 "ubuntu"
493 )
494 linter.do_lint(juju_status)
495
496 errors = linter.output_collector["errors"]
497 assert len(errors) == 1
498 assert errors[0]["id"] == "config-eq-check"
499 assert errors[0]["application"] == "ubuntu-host"
500 assert errors[0]["rule"] == "fake-opt"
501 assert errors[0]["expected_value"] is False
502 assert errors[0]["actual_value"] is True
503
436 def test_config_neq_valid(self, linter, juju_status):504 def test_config_neq_valid(self, linter, juju_status):
437 """Test the config condition 'neq'."""505 """Test the config condition 'neq'."""
438 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"neq": "foo"}}}506 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"neq": "foo"}}}

Subscribers

People subscribed via source and target branches