Merge ~gabrielcocenza/charm-juju-lint:bug/1993334 into charm-juju-lint:master

Proposed by Gabriel Cocenza
Status: Merged
Approved by: Eric Chen
Approved revision: 40c6b015e56aa08e615b4562c82a42123601787a
Merged at revision: 89392d23f89b265235937a6dc4236f6f1deb9e16
Proposed branch: ~gabrielcocenza/charm-juju-lint:bug/1993334
Merge into: charm-juju-lint:master
Diff against target: 162 lines (+51/-13)
4 files modified
lib/lib_jujulint.py (+15/-6)
scripts/templates/auto_lint.py (+2/-2)
tests/unit/test_auto_lint.py (+4/-4)
tests/unit/test_charm.py (+30/-1)
Reviewer Review Type Date Requested Status
Mert Kirpici (community) Approve
Erhan Sunar (community) Approve
🤖 prod-jenkaas-bootstack continuous-integration Approve
BootStack Reviewers Pending
Review via email: mp+431924@code.launchpad.net

Commit message

add timeout for running auto_lint.py

- hook enter into error state instead of holding machine lock
- change juju loop(deprecated) to jasyncio

Closes-Bug: #1993334

To post a comment you must log in.
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
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
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Mert Kirpici (mertkirpici) wrote :

Hi there Gabriel, thanks for the patch. It is looking good in my opinion.
I just left a question inline about the PID files.

Revision history for this message
Erhan Sunar (esunar) wrote :

LGTM

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

Thanks for the review Mert and Erhan.

I left comments on your questions.

Revision history for this message
Mert Kirpici (mertkirpici) wrote :

Thanks Gabriel for the detailed explanation. I left a comment, this is a +1 from me.

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

Change successfully merged at revision 89392d23f89b265235937a6dc4236f6f1deb9e16

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lib_jujulint.py b/lib/lib_jujulint.py
2index 0085aee..978aac7 100644
3--- a/lib/lib_jujulint.py
4+++ b/lib/lib_jujulint.py
5@@ -33,6 +33,7 @@ class Paths:
6 AUTO_LINT_SCRIPT_PATH = USR_LOCAL / "bin/auto_lint.py"
7 AUTO_LINT_CRONTAB_PATH = pathlib.Path("/etc/cron.d/juju-lint")
8 NAGIOS_PLUGINS_DIR = USR_LOCAL / "lib/nagios/plugins/"
9+ PID_FILENAME = pathlib.Path("/tmp/auto_lint.pid")
10
11
12 CHECK_SHORTNAME = "juju_lint_results"
13@@ -170,17 +171,25 @@ class JujuLintHelper:
14 """Run auto lint."""
15 try:
16 lint_script = str(Paths.AUTO_LINT_SCRIPT_PATH)
17- subprocess.check_output([lint_script], stderr=subprocess.STDOUT).decode(
18- "utf8"
19- )
20+ subprocess.check_output(
21+ [lint_script], stderr=subprocess.STDOUT, timeout=60
22+ ).decode("utf8")
23 except subprocess.CalledProcessError as error:
24 # here we care about the output only if it fails
25 logging.error(
26- "auto_lint.py failed with this output:\n{}".format(
27- error.output.decode("utf8")
28- )
29+ "auto_lint.py failed with this output:\n %s",
30+ error.output.decode("utf8"),
31+ )
32+ raise error
33+ except subprocess.TimeoutExpired as error:
34+ logging.error(
35+ "auto_lint.py timeout with this output:\n %s",
36+ error.output.decode("utf8"),
37 )
38 raise error
39+ finally:
40+ if Paths.PID_FILENAME.exists():
41+ Paths.PID_FILENAME.unlink()
42
43 def update_nrpe_config(self, current_checks):
44 """Run on relation config changed event."""
45diff --git a/scripts/templates/auto_lint.py b/scripts/templates/auto_lint.py
46index 5eb7b7b..65c76fc 100755
47--- a/scripts/templates/auto_lint.py
48+++ b/scripts/templates/auto_lint.py
49@@ -31,7 +31,7 @@ import sys
50 # The path below is templated in during charm install
51 sys.path.append("REPLACE_CHARMDIR/venv")
52
53-from juju import loop # noqa E402
54+from juju import jasyncio # noqa E402
55 from juju.model import Model # noqa E402
56 from juju.client.facade import TypeEncoder # noqa E402
57
58@@ -267,7 +267,7 @@ def main():
59 try:
60 auto_lint_config = read_json_file(LINT_CONFIG_PATH)
61 verify_auto_lint_config(auto_lint_config)
62- juju_status = loop.run(get_juju_status(auto_lint_config))
63+ juju_status = jasyncio.run(get_juju_status(auto_lint_config))
64 write_file(
65 JUJU_STATUS_PATH, json.dumps(juju_status, cls=TypeEncoder, sort_keys=True)
66 )
67diff --git a/tests/unit/test_auto_lint.py b/tests/unit/test_auto_lint.py
68index 53d4fbf..72a663a 100644
69--- a/tests/unit/test_auto_lint.py
70+++ b/tests/unit/test_auto_lint.py
71@@ -3,7 +3,7 @@ import copy
72 import unittest
73
74 import mock
75-from juju import loop
76+from juju import jasyncio
77 from scripts.templates.auto_lint import (
78 add_charm_config_to_juju_status,
79 lint_juju,
80@@ -82,7 +82,7 @@ class TestAutoLint(unittest.TestCase):
81 It can be removed if this is implemented in juju, reference bug:
82 https://bugs.launchpad.net/juju/+bug/1929911
83 """
84- juju_status = loop.run(
85+ juju_status = jasyncio.run(
86 add_charm_config_to_juju_status(self.juju_status, fixtures.MockModel)
87 )
88 for app_name, app_dict in juju_status["applications"].items():
89@@ -98,7 +98,7 @@ class TestAutoLint(unittest.TestCase):
90
91 def test_add_config_to_status_unset(self):
92 """Tests that the unset options are ignored."""
93- juju_status = loop.run(
94+ juju_status = jasyncio.run(
95 add_charm_config_to_juju_status(self.juju_status, fixtures.MockModelUnset)
96 )
97 for app_name, app_dict in juju_status["applications"].items():
98@@ -109,7 +109,7 @@ class TestAutoLint(unittest.TestCase):
99 """Tests handling the case where an option is missing a value."""
100
101 def test_helper():
102- loop.run(
103+ jasyncio.run(
104 add_charm_config_to_juju_status(
105 self.juju_status, fixtures.MockModelMissingValue
106 )
107diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
108index ee4e1e2..0786165 100644
109--- a/tests/unit/test_charm.py
110+++ b/tests/unit/test_charm.py
111@@ -2,6 +2,7 @@
112 import os
113 import json
114 import pathlib
115+import subprocess
116 import tempfile
117 import unittest
118
119@@ -158,7 +159,7 @@ class TestCharm(unittest.TestCase):
120 """Test: change crontab configuration."""
121 self.harness.begin()
122 self.harness.charm.state.installed = True
123- self.harness.update_config({"lint-frequency": "100"})
124+ self.harness.update_config({"lint-frequency": 100})
125 self.assertEqual(self.get_notice_count("config_changed"), 0)
126 with Paths.AUTO_LINT_CRONTAB_PATH.open() as f:
127 self.assertRegex(f.read(), r"\*/100 \*")
128@@ -263,6 +264,34 @@ class TestCharm(unittest.TestCase):
129 notice_count += 1
130 return notice_count
131
132+ @mock.patch("lib_jujulint.Paths.PID_FILENAME")
133+ @mock.patch("lib_jujulint.subprocess.check_output")
134+ def test_run_auto_lint_time_out(self, mock_check_output, mock_pid_file):
135+ """Test time out exception."""
136+ self.harness.begin()
137+ mock_check_output.side_effect = subprocess.TimeoutExpired(
138+ "/usr/local/bin/auto_lint.py",
139+ 60,
140+ output=(
141+ "Command '/usr/local/bin/auto_lint.py' timed out "
142+ "after 60 seconds".encode()
143+ ),
144+ )
145+ # with pid file
146+ mock_pid_file.exists.return_value = True
147+ with self.assertRaises(subprocess.TimeoutExpired):
148+ self.harness.charm.helper.run_auto_lint()
149+ mock_pid_file.assert_has_calls(
150+ [unittest.mock.call.exists(), unittest.mock.call.unlink()], any_order=True
151+ )
152+ mock_pid_file.reset_mock()
153+
154+ # without pid file
155+ mock_pid_file.exists.return_value = False
156+ with self.assertRaises(subprocess.TimeoutExpired):
157+ self.harness.charm.helper.run_auto_lint()
158+ mock_pid_file.assert_has_calls([unittest.mock.call.exists()], any_order=True)
159+
160
161 if __name__ == "__main__":
162 unittest.main()

Subscribers

People subscribed via source and target branches

to all changes: