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.
Revision history for this message
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)
Revision history for this message
Juju Gui Bot (juju-gui-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmworld/health.py'
--- charmworld/health.py 2013-08-16 17:55:44 +0000
+++ charmworld/health.py 2013-10-30 14:03:50 +0000
@@ -9,21 +9,33 @@
9FAIL = 'Fail'9FAIL = 'Fail'
1010
1111
12def check_mongodb(request):12def _check_collection_size(request, collection_name):
13 """Return a Check that a mongodb collection is not empty."""
14 status = FAIL
15 try:
16 count = request.db[collection_name].count()
17 if count > 0:
18 remark = '{0} {1} found'.format(count, collection_name)
19 status = PASS
20 else:
21 remark = 'There are no {0}. Is ingest running?'
22 remark = remark.format(collection_name)
23 except:
24 remark = 'The mongodb {0} collection is not available.'
25 remark = remark.format(collection_name)
26 finally:
27 check = Check('{0} collection'.format(collection_name), status, remark)
28 return check
29
30
31def check_charms_collection(request):
13 """Return a Check of the mongodb charms collection."""32 """Return a Check of the mongodb charms collection."""
14 status = FAIL33 return _check_collection_size(request, 'charms')
15 try:34
16 count = request.db.charms.count()35
17 if count > 0:36def check_bundles_collection(request):
18 remark = '{0} charms found'.format(count)37 """Return a Check of the mongodb bundles collection."""
19 status = PASS38 return _check_collection_size(request, 'bundles')
20 else:
21 remark = 'There are no charms. Is ingest running?'
22 except:
23 remark = 'The mongodb charm collection is not available.'
24 finally:
25 check = Check('charms collection', status, remark)
26 return check
2739
2840
29def check_elasticsearch(request):41def check_elasticsearch(request):
@@ -83,3 +95,41 @@
83 finally:95 finally:
84 check = Check('API3 interesting', status, remark)96 check = Check('API3 interesting', status, remark)
85 return check97 return check
98
99
100def check_collections(request):
101 """Return a Check of the collections that should exist in the Mongo DB."""
102 status = FAIL
103 required = set((
104 'basket-queue', 'baskets', 'bundles', 'charm-queue', 'charms'))
105 try:
106 existing = set(request.db.collection_names())
107 missing = required.difference(existing)
108 if missing:
109 missing = ', '.join(sorted(missing))
110 remark = 'Required collections do not (yet) exist: {0}'
111 remark = remark.format(missing)
112 else:
113 status = PASS
114 remark = 'All required collections exist.'
115 except:
116 remark = "Cannot query the MongoDB's collection names."
117 finally:
118 check = Check('MongoDB collections', status, remark)
119 return check
120
121
122def check_ingest_queues(request):
123 """Check the size of the ingest queues."""
124 status = FAIL
125 try:
126 charm_queue_size = request.db['charm-queue'].count()
127 basket_queue_size = request.db['basket-queue'].count()
128 status = PASS
129 remark = 'queued charms: {0}, queued baskets: {1}.'
130 remark = remark.format(charm_queue_size, basket_queue_size)
131 except:
132 remark = 'Cannot query the ingest queues.'
133 finally:
134 check = Check('Ingest queue sizes', status, remark)
135 return check
86136
=== modified file 'charmworld/tests/test_health.py'
--- charmworld/tests/test_health.py 2013-08-15 21:31:37 +0000
+++ charmworld/tests/test_health.py 2013-10-30 14:03:50 +0000
@@ -6,8 +6,11 @@
6from charmworld.health import (6from charmworld.health import (
7 check_api2,7 check_api2,
8 check_api3,8 check_api3,
9 check_bundles_collection,
10 check_charms_collection,
11 check_collections,
9 check_elasticsearch,12 check_elasticsearch,
10 check_mongodb,13 check_ingest_queues,
11)14)
12from charmworld.models import FeaturedSource15from charmworld.models import FeaturedSource
13from charmworld.testing import (16from charmworld.testing import (
@@ -33,23 +36,40 @@
3336
34class HealthTestCase(ViewTestBase, TestCheckMixin):37class HealthTestCase(ViewTestBase, TestCheckMixin):
3538
36 def test_check_mongodb_pass(self):39 def test_check_charms_collection_pass(self):
37 factory.makeCharm(self.db)40 factory.makeCharm(self.db)
38 check = check_mongodb(self.getRequest())41 check = check_charms_collection(self.getRequest())
39 remark = '1 charms found'42 remark = '1 charms found'
40 self.assertCheck(check, 'charms collection', 'Pass', remark)43 self.assertCheck(check, 'charms collection', 'Pass', remark)
4144
42 def test_check_mongodb_missing_data_fail(self):45 def test_check_charms_collection_missing_data_fail(self):
43 check = check_mongodb(self.getRequest())46 check = check_charms_collection(self.getRequest())
44 remark = 'There are no charms. Is ingest running?'47 remark = 'There are no charms. Is ingest running?'
45 self.assertCheck(check, 'charms collection', 'Fail', remark)48 self.assertCheck(check, 'charms collection', 'Fail', remark)
4649
47 def test_check_mongodb_no_collection_fail(self):50 def test_check_charms_collection_no_collection_fail(self):
48 request = self.getRequest()51 request = self.getRequest()
49 with patch.object(request, 'db', new_callable=self.makeFailCallable):52 with patch.object(request, 'db', new_callable=self.makeFailCallable):
50 check = check_mongodb(request)53 check = check_charms_collection(request)
51 self.assertCheck(check, 'charms collection', 'Fail')54 self.assertCheck(check, 'charms collection', 'Fail')
5255
56 def test_check_bundles_collection_pass(self):
57 factory.makeBundle(self.db)
58 check = check_bundles_collection(self.getRequest())
59 remark = '1 bundles found'
60 self.assertCheck(check, 'bundles collection', 'Pass', remark)
61
62 def test_check_bundles_collection_missing_data_fail(self):
63 check = check_bundles_collection(self.getRequest())
64 remark = 'There are no bundles. Is ingest running?'
65 self.assertCheck(check, 'bundles collection', 'Fail', remark)
66
67 def test_check_bundles_collection_no_collection_fail(self):
68 request = self.getRequest()
69 with patch.object(request, 'db', new_callable=self.makeFailCallable):
70 check = check_bundles_collection(request)
71 self.assertCheck(check, 'bundles collection', 'Fail')
72
53 def test_check_elasticsearch_pass(self):73 def test_check_elasticsearch_pass(self):
54 charm_data = factory.makeCharm(self.db)[1]74 charm_data = factory.makeCharm(self.db)[1]
55 self.use_index_client()75 self.use_index_client()
@@ -112,3 +132,52 @@
112132
113 def test_check_api3_no_matches_fail(self):133 def test_check_api3_no_matches_fail(self):
114 self._check_api_no_matches_fail(check_api3, 3)134 self._check_api_no_matches_fail(check_api3, 3)
135
136 def test_check_collections_pass(self):
137 request = self.getRequest()
138
139 def make_all_collection_names():
140 def all_collection_names():
141 return [
142 'fs.chunks', 'system.indexes', 'basket-queue', 'baskets',
143 'bundles', 'charm-queue', 'charms']
144 return all_collection_names
145
146 with patch.object(request.db, 'collection_names',
147 new_callable=make_all_collection_names):
148 check = check_collections(request)
149 self.assertCheck(
150 check, 'MongoDB collections', 'Pass',
151 'All required collections exist.')
152
153 def test_check_collections_missing_collections(self):
154 request = self.getRequest()
155 check = check_collections(request)
156 self.assertCheck(
157 check, 'MongoDB collections', 'Fail',
158 'Required collections do not (yet) exist: basket-queue, baskets, '
159 'bundles, charm-queue, charms')
160
161 def test_check_collections_db_error(self):
162 request = self.getRequest()
163 with patch.object(request.db, 'collection_names',
164 new_callable=self.makeFailCallable):
165 check = check_collections(request)
166 self.assertCheck(
167 check, 'MongoDB collections', 'Fail',
168 "Cannot query the MongoDB's collection names.")
169
170 def test_check_ingest_queues(self):
171 request = self.getRequest()
172 check = check_ingest_queues(request)
173 self.assertCheck(
174 check, 'Ingest queue sizes', 'Pass',
175 'queued charms: 0, queued baskets: 0.')
176
177 def test_check_ingest_queues_db_error(self):
178 request = self.getRequest()
179 with patch.object(request, 'db', new_callable=self.makeFailCallable):
180 check = check_ingest_queues(request)
181 self.assertCheck(
182 check, 'Ingest queue sizes', 'Fail',
183 'Cannot query the ingest queues.')
115184
=== modified file 'charmworld/views/misc.py'
--- charmworld/views/misc.py 2013-08-16 00:29:11 +0000
+++ charmworld/views/misc.py 2013-10-30 14:03:50 +0000
@@ -9,8 +9,11 @@
9from charmworld.health import (9from charmworld.health import (
10 check_api2,10 check_api2,
11 check_api3,11 check_api3,
12 check_bundles_collection,
13 check_charms_collection,
14 check_collections,
12 check_elasticsearch,15 check_elasticsearch,
13 check_mongodb,16 check_ingest_queues,
14)17)
1518
1619
@@ -48,8 +51,11 @@
48def heartbeat(request):51def heartbeat(request):
49 """Return a dict summarising the operational state of charmworld."""52 """Return a dict summarising the operational state of charmworld."""
50 checks = []53 checks = []
51 checks.append(check_mongodb(request))54 checks.append(check_charms_collection(request))
55 checks.append(check_bundles_collection(request))
52 checks.append(check_elasticsearch(request))56 checks.append(check_elasticsearch(request))
53 checks.append(check_api2(request))57 checks.append(check_api2(request))
54 checks.append(check_api3(request))58 checks.append(check_api3(request))
59 checks.append(check_collections(request))
60 checks.append(check_ingest_queues(request))
55 return {'checks': checks}61 return {'checks': checks}
5662
=== modified file 'charmworld/views/tests/test_misc.py'
--- charmworld/views/tests/test_misc.py 2013-08-15 20:28:10 +0000
+++ charmworld/views/tests/test_misc.py 2013-10-30 14:03:50 +0000
@@ -109,18 +109,38 @@
109 def test_all_checks_pass(self):109 def test_all_checks_pass(self):
110 # All checks pass when the services are available and data is returned.110 # All checks pass when the services are available and data is returned.
111 charm_data = factory.makeCharm(self.db, downloads=5)[1]111 charm_data = factory.makeCharm(self.db, downloads=5)[1]
112 factory.makeBundle(self.db)
112 self.use_index_client()113 self.use_index_client()
113 self.index_client.index_charm(charm_data)114 self.index_client.index_charm(charm_data)
114 FeaturedSource.from_db(self.db).set_featured(charm_data, 'charm')115 FeaturedSource.from_db(self.db).set_featured(charm_data, 'charm')
115 response = heartbeat(self.getRequest())116
117 def make_all_collection_names():
118 def all_collection_names():
119 return [
120 'fs.chunks', 'system.indexes', 'basket-queue', 'baskets',
121 'bundles', 'charm-queue', 'charms']
122 return all_collection_names
123
124 request = self.getRequest()
125 with patch.object(request.db, 'collection_names',
126 new_callable=make_all_collection_names):
127 response = heartbeat(request)
128
116 checks = response['checks']129 checks = response['checks']
130 self.assertEqual(7, len(checks))
117 remark = '1 charms found'131 remark = '1 charms found'
118 self.assertCheck(checks[0], 'charms collection', 'Pass', remark)132 self.assertCheck(checks[0], 'charms collection', 'Pass', remark)
133 remark = '1 bundles found'
134 self.assertCheck(checks[1], 'bundles collection', 'Pass', remark)
119 remark = '1 matches found'135 remark = '1 matches found'
120 self.assertCheck(checks[1], 'charms index', 'Pass', remark)136 self.assertCheck(checks[2], 'charms index', 'Pass', remark)
121 remark = 'Interesting has new, popular, and featured.'137 remark = 'Interesting has new, popular, and featured.'
122 self.assertCheck(checks[2], 'API2 interesting', 'Pass', remark)138 self.assertCheck(checks[3], 'API2 interesting', 'Pass', remark)
123 self.assertCheck(checks[3], 'API3 interesting', 'Pass', remark)139 self.assertCheck(checks[4], 'API3 interesting', 'Pass', remark)
140 remark = 'All required collections exist.'
141 self.assertCheck(checks[5], 'MongoDB collections', 'Pass', remark)
142 remark = 'queued charms: 0, queued baskets: 0.'
143 self.assertCheck(checks[6], 'Ingest queue sizes', 'Pass', remark)
124144
125 def test_checks_fail(self):145 def test_checks_fail(self):
126 # When services or data are not available, the checks fail.146 # When services or data are not available, the checks fail.
@@ -128,11 +148,20 @@
128 # are not available148 # are not available
129 response = heartbeat(self.getRequest())149 response = heartbeat(self.getRequest())
130 checks = response['checks']150 checks = response['checks']
151 self.assertEqual(7, len(checks))
131 remark = 'There are no charms. Is ingest running?'152 remark = 'There are no charms. Is ingest running?'
132 self.assertCheck(checks[0], 'charms collection', 'Fail', remark)153 self.assertCheck(checks[0], 'charms collection', 'Fail', remark)
154 remark = 'There are no bundles. Is ingest running?'
155 self.assertCheck(checks[1], 'bundles collection', 'Fail', remark)
133 remark = 'The elasticsearch charm index is not available.'156 remark = 'The elasticsearch charm index is not available.'
134 self.assertCheck(checks[1], 'charms index', 'Fail', remark)157 self.assertCheck(checks[2], 'charms index', 'Fail', remark)
135 remark = 'The API2 is not available.'158 remark = 'The API2 is not available.'
136 self.assertCheck(checks[2], 'API2 interesting', 'Fail', remark)159 self.assertCheck(checks[3], 'API2 interesting', 'Fail', remark)
137 remark = 'The API3 is not available.'160 remark = 'The API3 is not available.'
138 self.assertCheck(checks[3], 'API3 interesting', 'Fail', remark)161 self.assertCheck(checks[4], 'API3 interesting', 'Fail', remark)
162 remark = (
163 'Required collections do not (yet) exist: basket-queue, '
164 'baskets, bundles, charm-queue, charms')
165 self.assertCheck(checks[5], 'MongoDB collections', 'Fail', remark)
166 remark = 'queued charms: 0, queued baskets: 0.'
167 self.assertCheck(checks[6], 'Ingest queue sizes', 'Pass', remark)

Subscribers

People subscribed via source and target branches