Merge ~gabrielcocenza/juju-lint:bug/1939748 into juju-lint:master

Proposed by Gabriel Cocenza
Status: Merged
Approved by: Martin Kalcok
Approved revision: 1c967a11818dc4cd335d6f60c86bf092edb60695
Merged at revision: ce2c06aec8a1268a9d340c1f05d0a61add1966ff
Proposed branch: ~gabrielcocenza/juju-lint:bug/1939748
Merge into: juju-lint:master
Diff against target: 118 lines (+77/-1)
3 files modified
contrib/includes/openstack.yaml (+2/-0)
jujulint/lint.py (+44/-1)
tests/test_jujulint.py (+31/-0)
Reviewer Review Type Date Requested Status
Martin Kalcok (community) Approve
Paul Goins Approve
Review via email: mp+425830@code.launchpad.net

Commit message

added new config check "search" using regex.

- updated openstack rules for rabbitmq-server to check queue_thresholds.

- added unit tests for "search" config check.

LP #1939748

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
Martin Kalcok (martin-kalcok) wrote :

I'm not sure I'm completely convinced with the `inside` operator. I find the naming itself bit confusing. Current syntax

config_name:
  inside: "some_string"

sounds like the config value should be "substring" of the value that's in the `inside` directive. Alternative name could be "includes" ?

Secondly, If we want to add more flexible option than those that currently exist, maybe we should go for a operator that would match regexp. Example:

config_name:
  match: "regexp"

this would imo add more flexibility than `inside`.

I would also maybe check with the bootstack team about how often they set specific thresholds for individual queues. Because if most of the deployments just setup thresholds with wildcards, we can fix this by simply using.

queue_thresholds:
  eq: "[['\*', '\*', 25000, 27500]]"

That way we could exclude the discussion about the new operator to new ticket/issue.

review: Needs Information
Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Thanks for your review Martin.

I do agree with you that inside wasn't the best naming for that and the regex can be better explored for other use cases that might be necessary to the future.

I just changed the name from "match" as you proposed to "search" because the operator `eq` uses `re.match` in the `helper_operator_eq_check` and can be a little confusing for someone that is looking the code. The new `search` operator uses `re.search` so I think the naming might be better at this situation.

Revision history for this message
Paul Goins (vultaire) wrote :

I generally like the change, and it makes sense having a regex match.

My main concern is re: a few of the strings which will be output on error.

I'll give a +1 - one more +1 is still required. However, I left a comment which I hope you'll consider.

review: Approve
Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Thanks for your review Paul. I agree that the messages were awkward when using the `check_config_generic`

Revision history for this message
Paul Goins (vultaire) wrote :

I like it. +1; thank you!

review: Approve
Revision history for this message
Martin Kalcok (martin-kalcok) wrote :

LGTM as well +1...Thank you Gabriel.

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

Change successfully merged at revision ce2c06aec8a1268a9d340c1f05d0a61add1966ff

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/contrib/includes/openstack.yaml b/contrib/includes/openstack.yaml
2index 5235aea..f7b2a85 100644
3--- a/contrib/includes/openstack.yaml
4+++ b/contrib/includes/openstack.yaml
5@@ -21,6 +21,8 @@ openstack config base: &openstack-config-base
6 rabbitmq-server:
7 cluster-partition-handling:
8 eq: "pause_minority"
9+ queue_thresholds:
10+ search: "\\W\\*, \\W\\*, 25000, 27500"
11 keystone:
12 token-expiration:
13 gte: 86400
14diff --git a/jujulint/lint.py b/jujulint/lint.py
15index 748fc8a..3b7cfd3 100755
16--- a/jujulint/lint.py
17+++ b/jujulint/lint.py
18@@ -37,7 +37,7 @@ import jujulint.util as utils
19 from jujulint.check_spaces import Relation, find_space_mismatches
20 from jujulint.logging import Logger
21
22-VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte")
23+VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte", "search")
24
25 # Generic named tuple to represent the binary config operators (eq,neq,gte)
26 ConfigOperator = collections.namedtuple(
27@@ -291,6 +291,49 @@ class Linter:
28 operator, app_name, check_value, config_key, app_config
29 )
30
31+ def search(self, app_name, check_value, config_key, app_config):
32+ """Scan through the charm config looking for a match using the regex pattern."""
33+ actual_value = app_config.get(config_key)
34+ if actual_value:
35+ if re.search(str(check_value), str(actual_value)):
36+ self._log_with_header(
37+ "Application {} has a valid config for '{}': regex {} found at {}".format(
38+ app_name,
39+ config_key,
40+ repr(check_value),
41+ repr(actual_value),
42+ )
43+ )
44+ return True
45+ self.handle_error(
46+ {
47+ "id": "config-search-check",
48+ "tags": ["config", "search"],
49+ "description": "Checks for config condition 'search'",
50+ "application": app_name,
51+ "rule": config_key,
52+ "expected_value": check_value,
53+ "actual_value": actual_value,
54+ "message": "Application {} has an invalid config for '{}': regex {} not found at {}".format(
55+ app_name,
56+ config_key,
57+ repr(check_value),
58+ repr(actual_value),
59+ ),
60+ }
61+ )
62+ return False
63+
64+ self._log_with_header(
65+ "Application {} has no config for '{}', can't search the regex pattern {}.".format(
66+ app_name,
67+ config_key,
68+ repr(check_value),
69+ ),
70+ level=logging.WARN,
71+ )
72+ return False
73+
74 def check_config_generic(
75 self, operator, app_name, check_value, config_key, app_config
76 ):
77diff --git a/tests/test_jujulint.py b/tests/test_jujulint.py
78index 11cc514..3603146 100644
79--- a/tests/test_jujulint.py
80+++ b/tests/test_jujulint.py
81@@ -603,6 +603,37 @@ applications:
82 assert errors[0]["application"] == "ubuntu"
83 assert errors[0]["rule"] == "fake-opt"
84
85+ def test_config_search_valid(self, linter, juju_status):
86+ """Test the config condition 'search' when valid."""
87+ linter.lint_rules["config"] = {
88+ "ubuntu": {"fake_opt": {"search": "\\W\\*, \\W\\*, 25000, 27500"}}
89+ }
90+ juju_status["applications"]["ubuntu"]["options"] = {
91+ "fake_opt": "[[/, queue1, 10, 20], [\\*, \\*, 25000, 27500]]"
92+ }
93+ linter.do_lint(juju_status)
94+
95+ errors = linter.output_collector["errors"]
96+ assert len(errors) == 0
97+
98+ def test_config_search_invalid(self, linter, juju_status):
99+ """Test the config condition 'search' when invalid."""
100+ linter.lint_rules["config"] = {
101+ "ubuntu": {"fake_opt": {"search": "\\W\\*, \\W\\*, 25000, 27500"}}
102+ }
103+ juju_status["applications"]["ubuntu"]["options"] = {
104+ "fake_opt": "[[/, queue1, 10, 20], [\\*, \\*, 10, 20]]"
105+ }
106+ linter.do_lint(juju_status)
107+
108+ errors = linter.output_collector["errors"]
109+ assert len(errors) == 1
110+ assert errors[0]["id"] == "config-search-check"
111+ assert errors[0]["application"] == "ubuntu"
112+ assert errors[0]["rule"] == "fake_opt"
113+ assert errors[0]["expected_value"] == "\\W\\*, \\W\\*, 25000, 27500"
114+ assert errors[0]["actual_value"] == "[[/, queue1, 10, 20], [\\*, \\*, 10, 20]]"
115+
116 def test_parse_cmr_apps_export_bundle(self, linter):
117 """Test the charm CMR parsing for bundles."""
118 parsed_yaml = {

Subscribers

People subscribed via source and target branches