Merge lp:~adeuring/charmworld/more-heartbeat-info into lp:~juju-jitsu/charmworld/trunk

Proposed by Abel Deuring
Status: Merged
Approved by: Abel Deuring
Approved revision: 434
Merged at revision: 433
Proposed branch: lp:~adeuring/charmworld/more-heartbeat-info
Merge into: lp:~juju-jitsu/charmworld/trunk
Diff against target: 311 lines (+184/-30)
4 files modified
charmworld/health.py (+64/-14)
charmworld/tests/test_health.py (+76/-7)
charmworld/views/misc.py (+8/-2)
charmworld/views/tests/test_misc.py (+36/-7)
To merge this branch: bzr merge lp:~adeuring/charmworld/more-heartbeat-info
Reviewer Review Type Date Requested Status
Juju Gui Bot continuous-integration Approve
Curtis Hovey (community) code Approve
Review via email: mp+193248@code.launchpad.net

Commit message

heartbeat: Checks added that the bundles collection is not empty, that all MongoDB collections exist; show size of ingest queues.

Description of the change

This branch adds several details to charmworld's heartbeat page:

- Number of bundles
- Do all required collections exist?
- Number of entries in the ingest queues

Implementation and test are quite straightforward, I think

To post a comment you must log in.
Curtis Hovey (sinzui) wrote :

Thank you Abel. This is a good start. I think this branch is good to land.

Can you do a follow up branch to check some other collections?

REQUIRED BY PRODUCTION JUJU-GUI
featured
jenkins
qa

REQUIRED BY CHARMERS
review_queue
review_queue_latency
answer_queue
core_review_queue

KNOWING THE DB VERSION would be helpful
migration_version

review: Approve (code)
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmworld/health.py'
2--- charmworld/health.py 2013-08-16 17:55:44 +0000
3+++ charmworld/health.py 2013-10-30 14:03:50 +0000
4@@ -9,21 +9,33 @@
5 FAIL = 'Fail'
6
7
8-def check_mongodb(request):
9+def _check_collection_size(request, collection_name):
10+ """Return a Check that a mongodb collection is not empty."""
11+ status = FAIL
12+ try:
13+ count = request.db[collection_name].count()
14+ if count > 0:
15+ remark = '{0} {1} found'.format(count, collection_name)
16+ status = PASS
17+ else:
18+ remark = 'There are no {0}. Is ingest running?'
19+ remark = remark.format(collection_name)
20+ except:
21+ remark = 'The mongodb {0} collection is not available.'
22+ remark = remark.format(collection_name)
23+ finally:
24+ check = Check('{0} collection'.format(collection_name), status, remark)
25+ return check
26+
27+
28+def check_charms_collection(request):
29 """Return a Check of the mongodb charms collection."""
30- status = FAIL
31- try:
32- count = request.db.charms.count()
33- if count > 0:
34- remark = '{0} charms found'.format(count)
35- status = PASS
36- else:
37- remark = 'There are no charms. Is ingest running?'
38- except:
39- remark = 'The mongodb charm collection is not available.'
40- finally:
41- check = Check('charms collection', status, remark)
42- return check
43+ return _check_collection_size(request, 'charms')
44+
45+
46+def check_bundles_collection(request):
47+ """Return a Check of the mongodb bundles collection."""
48+ return _check_collection_size(request, 'bundles')
49
50
51 def check_elasticsearch(request):
52@@ -83,3 +95,41 @@
53 finally:
54 check = Check('API3 interesting', status, remark)
55 return check
56+
57+
58+def check_collections(request):
59+ """Return a Check of the collections that should exist in the Mongo DB."""
60+ status = FAIL
61+ required = set((
62+ 'basket-queue', 'baskets', 'bundles', 'charm-queue', 'charms'))
63+ try:
64+ existing = set(request.db.collection_names())
65+ missing = required.difference(existing)
66+ if missing:
67+ missing = ', '.join(sorted(missing))
68+ remark = 'Required collections do not (yet) exist: {0}'
69+ remark = remark.format(missing)
70+ else:
71+ status = PASS
72+ remark = 'All required collections exist.'
73+ except:
74+ remark = "Cannot query the MongoDB's collection names."
75+ finally:
76+ check = Check('MongoDB collections', status, remark)
77+ return check
78+
79+
80+def check_ingest_queues(request):
81+ """Check the size of the ingest queues."""
82+ status = FAIL
83+ try:
84+ charm_queue_size = request.db['charm-queue'].count()
85+ basket_queue_size = request.db['basket-queue'].count()
86+ status = PASS
87+ remark = 'queued charms: {0}, queued baskets: {1}.'
88+ remark = remark.format(charm_queue_size, basket_queue_size)
89+ except:
90+ remark = 'Cannot query the ingest queues.'
91+ finally:
92+ check = Check('Ingest queue sizes', status, remark)
93+ return check
94
95=== modified file 'charmworld/tests/test_health.py'
96--- charmworld/tests/test_health.py 2013-08-15 21:31:37 +0000
97+++ charmworld/tests/test_health.py 2013-10-30 14:03:50 +0000
98@@ -6,8 +6,11 @@
99 from charmworld.health import (
100 check_api2,
101 check_api3,
102+ check_bundles_collection,
103+ check_charms_collection,
104+ check_collections,
105 check_elasticsearch,
106- check_mongodb,
107+ check_ingest_queues,
108 )
109 from charmworld.models import FeaturedSource
110 from charmworld.testing import (
111@@ -33,23 +36,40 @@
112
113 class HealthTestCase(ViewTestBase, TestCheckMixin):
114
115- def test_check_mongodb_pass(self):
116+ def test_check_charms_collection_pass(self):
117 factory.makeCharm(self.db)
118- check = check_mongodb(self.getRequest())
119+ check = check_charms_collection(self.getRequest())
120 remark = '1 charms found'
121 self.assertCheck(check, 'charms collection', 'Pass', remark)
122
123- def test_check_mongodb_missing_data_fail(self):
124- check = check_mongodb(self.getRequest())
125+ def test_check_charms_collection_missing_data_fail(self):
126+ check = check_charms_collection(self.getRequest())
127 remark = 'There are no charms. Is ingest running?'
128 self.assertCheck(check, 'charms collection', 'Fail', remark)
129
130- def test_check_mongodb_no_collection_fail(self):
131+ def test_check_charms_collection_no_collection_fail(self):
132 request = self.getRequest()
133 with patch.object(request, 'db', new_callable=self.makeFailCallable):
134- check = check_mongodb(request)
135+ check = check_charms_collection(request)
136 self.assertCheck(check, 'charms collection', 'Fail')
137
138+ def test_check_bundles_collection_pass(self):
139+ factory.makeBundle(self.db)
140+ check = check_bundles_collection(self.getRequest())
141+ remark = '1 bundles found'
142+ self.assertCheck(check, 'bundles collection', 'Pass', remark)
143+
144+ def test_check_bundles_collection_missing_data_fail(self):
145+ check = check_bundles_collection(self.getRequest())
146+ remark = 'There are no bundles. Is ingest running?'
147+ self.assertCheck(check, 'bundles collection', 'Fail', remark)
148+
149+ def test_check_bundles_collection_no_collection_fail(self):
150+ request = self.getRequest()
151+ with patch.object(request, 'db', new_callable=self.makeFailCallable):
152+ check = check_bundles_collection(request)
153+ self.assertCheck(check, 'bundles collection', 'Fail')
154+
155 def test_check_elasticsearch_pass(self):
156 charm_data = factory.makeCharm(self.db)[1]
157 self.use_index_client()
158@@ -112,3 +132,52 @@
159
160 def test_check_api3_no_matches_fail(self):
161 self._check_api_no_matches_fail(check_api3, 3)
162+
163+ def test_check_collections_pass(self):
164+ request = self.getRequest()
165+
166+ def make_all_collection_names():
167+ def all_collection_names():
168+ return [
169+ 'fs.chunks', 'system.indexes', 'basket-queue', 'baskets',
170+ 'bundles', 'charm-queue', 'charms']
171+ return all_collection_names
172+
173+ with patch.object(request.db, 'collection_names',
174+ new_callable=make_all_collection_names):
175+ check = check_collections(request)
176+ self.assertCheck(
177+ check, 'MongoDB collections', 'Pass',
178+ 'All required collections exist.')
179+
180+ def test_check_collections_missing_collections(self):
181+ request = self.getRequest()
182+ check = check_collections(request)
183+ self.assertCheck(
184+ check, 'MongoDB collections', 'Fail',
185+ 'Required collections do not (yet) exist: basket-queue, baskets, '
186+ 'bundles, charm-queue, charms')
187+
188+ def test_check_collections_db_error(self):
189+ request = self.getRequest()
190+ with patch.object(request.db, 'collection_names',
191+ new_callable=self.makeFailCallable):
192+ check = check_collections(request)
193+ self.assertCheck(
194+ check, 'MongoDB collections', 'Fail',
195+ "Cannot query the MongoDB's collection names.")
196+
197+ def test_check_ingest_queues(self):
198+ request = self.getRequest()
199+ check = check_ingest_queues(request)
200+ self.assertCheck(
201+ check, 'Ingest queue sizes', 'Pass',
202+ 'queued charms: 0, queued baskets: 0.')
203+
204+ def test_check_ingest_queues_db_error(self):
205+ request = self.getRequest()
206+ with patch.object(request, 'db', new_callable=self.makeFailCallable):
207+ check = check_ingest_queues(request)
208+ self.assertCheck(
209+ check, 'Ingest queue sizes', 'Fail',
210+ 'Cannot query the ingest queues.')
211
212=== modified file 'charmworld/views/misc.py'
213--- charmworld/views/misc.py 2013-08-16 00:29:11 +0000
214+++ charmworld/views/misc.py 2013-10-30 14:03:50 +0000
215@@ -9,8 +9,11 @@
216 from charmworld.health import (
217 check_api2,
218 check_api3,
219+ check_bundles_collection,
220+ check_charms_collection,
221+ check_collections,
222 check_elasticsearch,
223- check_mongodb,
224+ check_ingest_queues,
225 )
226
227
228@@ -48,8 +51,11 @@
229 def heartbeat(request):
230 """Return a dict summarising the operational state of charmworld."""
231 checks = []
232- checks.append(check_mongodb(request))
233+ checks.append(check_charms_collection(request))
234+ checks.append(check_bundles_collection(request))
235 checks.append(check_elasticsearch(request))
236 checks.append(check_api2(request))
237 checks.append(check_api3(request))
238+ checks.append(check_collections(request))
239+ checks.append(check_ingest_queues(request))
240 return {'checks': checks}
241
242=== modified file 'charmworld/views/tests/test_misc.py'
243--- charmworld/views/tests/test_misc.py 2013-08-15 20:28:10 +0000
244+++ charmworld/views/tests/test_misc.py 2013-10-30 14:03:50 +0000
245@@ -109,18 +109,38 @@
246 def test_all_checks_pass(self):
247 # All checks pass when the services are available and data is returned.
248 charm_data = factory.makeCharm(self.db, downloads=5)[1]
249+ factory.makeBundle(self.db)
250 self.use_index_client()
251 self.index_client.index_charm(charm_data)
252 FeaturedSource.from_db(self.db).set_featured(charm_data, 'charm')
253- response = heartbeat(self.getRequest())
254+
255+ def make_all_collection_names():
256+ def all_collection_names():
257+ return [
258+ 'fs.chunks', 'system.indexes', 'basket-queue', 'baskets',
259+ 'bundles', 'charm-queue', 'charms']
260+ return all_collection_names
261+
262+ request = self.getRequest()
263+ with patch.object(request.db, 'collection_names',
264+ new_callable=make_all_collection_names):
265+ response = heartbeat(request)
266+
267 checks = response['checks']
268+ self.assertEqual(7, len(checks))
269 remark = '1 charms found'
270 self.assertCheck(checks[0], 'charms collection', 'Pass', remark)
271+ remark = '1 bundles found'
272+ self.assertCheck(checks[1], 'bundles collection', 'Pass', remark)
273 remark = '1 matches found'
274- self.assertCheck(checks[1], 'charms index', 'Pass', remark)
275+ self.assertCheck(checks[2], 'charms index', 'Pass', remark)
276 remark = 'Interesting has new, popular, and featured.'
277- self.assertCheck(checks[2], 'API2 interesting', 'Pass', remark)
278- self.assertCheck(checks[3], 'API3 interesting', 'Pass', remark)
279+ self.assertCheck(checks[3], 'API2 interesting', 'Pass', remark)
280+ self.assertCheck(checks[4], 'API3 interesting', 'Pass', remark)
281+ remark = 'All required collections exist.'
282+ self.assertCheck(checks[5], 'MongoDB collections', 'Pass', remark)
283+ remark = 'queued charms: 0, queued baskets: 0.'
284+ self.assertCheck(checks[6], 'Ingest queue sizes', 'Pass', remark)
285
286 def test_checks_fail(self):
287 # When services or data are not available, the checks fail.
288@@ -128,11 +148,20 @@
289 # are not available
290 response = heartbeat(self.getRequest())
291 checks = response['checks']
292+ self.assertEqual(7, len(checks))
293 remark = 'There are no charms. Is ingest running?'
294 self.assertCheck(checks[0], 'charms collection', 'Fail', remark)
295+ remark = 'There are no bundles. Is ingest running?'
296+ self.assertCheck(checks[1], 'bundles collection', 'Fail', remark)
297 remark = 'The elasticsearch charm index is not available.'
298- self.assertCheck(checks[1], 'charms index', 'Fail', remark)
299+ self.assertCheck(checks[2], 'charms index', 'Fail', remark)
300 remark = 'The API2 is not available.'
301- self.assertCheck(checks[2], 'API2 interesting', 'Fail', remark)
302+ self.assertCheck(checks[3], 'API2 interesting', 'Fail', remark)
303 remark = 'The API3 is not available.'
304- self.assertCheck(checks[3], 'API3 interesting', 'Fail', remark)
305+ self.assertCheck(checks[4], 'API3 interesting', 'Fail', remark)
306+ remark = (
307+ 'Required collections do not (yet) exist: basket-queue, '
308+ 'baskets, bundles, charm-queue, charms')
309+ self.assertCheck(checks[5], 'MongoDB collections', 'Fail', remark)
310+ remark = 'queued charms: 0, queued baskets: 0.'
311+ self.assertCheck(checks[6], 'Ingest queue sizes', 'Pass', remark)

Subscribers

People subscribed via source and target branches