Merge lp:~jcsackett/charmworld/better-jobs into lp:charmworld

Proposed by j.c.sackett
Status: Merged
Approved by: j.c.sackett
Approved revision: 471
Merged at revision: 464
Proposed branch: lp:~jcsackett/charmworld/better-jobs
Merge into: lp:charmworld
Diff against target: 195 lines (+61/-8)
5 files modified
charmworld/jobs/askubuntu.py (+7/-1)
charmworld/jobs/github.py (+16/-2)
charmworld/jobs/review.py (+7/-0)
charmworld/jobs/tests/test_askubuntu.py (+19/-2)
charmworld/jobs/tests/test_github.py (+12/-3)
To merge this branch: bzr merge lp:~jcsackett/charmworld/better-jobs
Reviewer Review Type Date Requested Status
Juju Gui Bot continuous-integration Approve
Aaron Bentley (community) Approve
Review via email: mp+195443@code.launchpad.net

Commit message

Handle some failure modes with better logging.

Description of the change

This branch adds some exception handling and logging to known failure modes we
want to keep track of, as well as some better request handling.

charmworld/jobs/askbubuntu.py
-----------------------------
* Added simple backoff parameter handling, which stackexchange API sets on some
  responses.
* Added handling of previous error condition wherein job couldn't connect to API
  servers. This is now logged as an error.

charmworld/jobs/github.py
-------------------------
* Added handling of limit rates in github requests, and logs instances of
  exceeding the limit as an error.
* Added handling of previous error condition wherein job couldn't connect to API
  servers. This is now logged as an error.

charmworld/jobs/review.py
-------------------------
* Logs missing lp_credentials file, ie a filepath is provided but no file
  exists. In this instance also sets the lp_credentials var to None so we'll
  login anonymously, rather than accidentally launching lynx.
* Logs whether the credentials file exists and we're logging in as charmbot, or
  doesn't exist and we're logging in anonymously.

To post a comment you must log in.
lp:~jcsackett/charmworld/better-jobs updated
468. By j.c.sackett

Merged trunk.

469. By j.c.sackett

Shut up, lint.

470. By j.c.sackett

Tests for askubuntu backoff.

471. By j.c.sackett

Tests actually, y'know, test things.

Revision history for this message
Aaron Bentley (abentley) wrote :

Looks good. Thanks for the tests.

review: Approve
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
1=== modified file 'charmworld/jobs/askubuntu.py'
2--- charmworld/jobs/askubuntu.py 2013-09-27 16:13:21 +0000
3+++ charmworld/jobs/askubuntu.py 2013-11-18 15:35:18 +0000
4@@ -30,6 +30,8 @@
5 def get_questions(page):
6 data = get_data_from_askubuntu(page)
7 results = data['items']
8+ backoff = int(data.get('backoff', 0))
9+ time.sleep(backoff)
10 if data['has_more']:
11 results += get_questions(page + 1)
12 return results
13@@ -73,7 +75,11 @@
14 conn = pymongo.Connection(MONGO_URL, safe=True)
15 db = conn['juju']
16 settings = get_ini()
17- questions = get_all_questions(log)
18+ try:
19+ questions = get_all_questions(log)
20+ except requests.ConnectionError as e:
21+ log.error("Couldn't complete request: %s" % e)
22+ return
23 entries = create_entries_from_questions(questions)
24 try:
25 with lock('askubuntu',
26
27=== modified file 'charmworld/jobs/github.py'
28--- charmworld/jobs/github.py 2013-10-10 17:59:16 +0000
29+++ charmworld/jobs/github.py 2013-11-18 15:35:18 +0000
30@@ -16,6 +16,10 @@
31 from utils import LockHeld
32
33
34+class GithubRateLimitError(Exception):
35+ pass
36+
37+
38 def make_github_request(endpoint_name, repo_name=None):
39 if endpoint_name == 'repos':
40 url = 'https://api.github.com/users/charms/repos'
41@@ -24,6 +28,8 @@
42 raise ValueError('repo_name cannot be None for pull requests')
43 url = 'https://api.github.com/repos/charms/%s/pulls' % repo_name
44 r = requests.get(url)
45+ if int(r.headers.get('x-ratelimit-remaining', 1)) == 0:
46+ raise GithubRateLimitError('Github ratelimit exceeded')
47 return r.json()
48
49
50@@ -77,8 +83,16 @@
51 conn = pymongo.Connection(MONGO_URL, safe=True)
52 db = conn['juju']
53 settings = get_ini()
54- charm_repos = get_charm_repos(log)
55- pull_requests = get_pull_requests(charm_repos, log)
56+ try:
57+ charm_repos = get_charm_repos(log)
58+ except (requests.ConnectionError, GithubRateLimitError) as e:
59+ log.error("Couldn't complete request for repos: %s" % e)
60+ return
61+ try:
62+ pull_requests = get_pull_requests(charm_repos, log)
63+ except (requests.ConnectionError, GithubRateLimitError) as e:
64+ log.error("Couldn't complete request for pull-requests: %s" % e)
65+ return
66 entries = create_entries_from_pull_requests(pull_requests)
67 try:
68 with lock('github',
69
70=== modified file 'charmworld/jobs/review.py'
71--- charmworld/jobs/review.py 2013-11-13 14:00:14 +0000
72+++ charmworld/jobs/review.py 2013-11-18 15:35:18 +0000
73@@ -17,6 +17,7 @@
74 import datetime
75 import itertools
76 import logging
77+import os
78
79 from launchpadlib.launchpad import Launchpad
80 import pymongo
81@@ -42,14 +43,20 @@
82 lp_credentials = get_ini()['lp_credentials_file']
83 except KeyError:
84 lp_credentials = None
85+ if not os.path.exists(lp_credentials):
86+ log.warn("lp_credentials path doesn't exist: %s" % lp_credentials)
87+ lp_credentials = None
88 with Timer() as timer:
89 if lp_credentials:
90+ log.info("Found lp_credentials; proceeding.")
91 lp = Launchpad.login_with(application_name='charm-tools',
92 service_root='production',
93 version='devel',
94 consumer_name='charmworld',
95 credentials_file=lp_credentials)
96 else:
97+ log.info(
98+ "Couldn't find lp_credentials; proceeding without latency.")
99 lp = Launchpad.login_anonymously('charm-tools',
100 'production',
101 version='devel')
102
103=== modified file 'charmworld/jobs/tests/test_askubuntu.py'
104--- charmworld/jobs/tests/test_askubuntu.py 2013-10-15 20:04:46 +0000
105+++ charmworld/jobs/tests/test_askubuntu.py 2013-11-18 15:35:18 +0000
106@@ -14,8 +14,10 @@
107
108 class FakeGet:
109
110- def __init__(self, has_more=False):
111+ def __init__(self, has_more=False, backoff=None):
112 self.response = {'items': [1, 2, 3], 'has_more': has_more}
113+ if backoff:
114+ self.response['backoff'] = backoff
115 self.call_count = 0
116
117 def __call__(self, *args, **kwargs):
118@@ -38,11 +40,26 @@
119 def test_get_questions_recursive(self):
120 with patch(
121 'charmworld.jobs.askubuntu.get_data_from_askubuntu',
122- FakeGet(True)) as mock:
123+ FakeGet(has_more=True)) as mock:
124 questions = get_questions(1)
125 self.assertEqual(2, mock.call_count)
126 self.assertEqual([1, 2, 3, 1, 2, 3], questions)
127
128+ def test_get_questions_handles_backoff(self):
129+ with patch(
130+ 'charmworld.jobs.askubuntu.get_data_from_askubuntu',
131+ FakeGet(backoff='3')):
132+ with patch('time.sleep') as time_mock:
133+ get_questions(1)
134+ time_mock.assert_called_once_with(3)
135+
136+ def test_get_questions_backoff_defaults_to_zero(self):
137+ with patch(
138+ 'charmworld.jobs.askubuntu.get_data_from_askubuntu', FakeGet()):
139+ with patch('time.sleep') as time_mock:
140+ get_questions(1)
141+ time_mock.assert_called_once_with(0)
142+
143 def test_create_entries_from_questions_empty(self):
144 entries = create_entries_from_questions([])
145 self.assertEqual([], entries)
146
147=== modified file 'charmworld/jobs/tests/test_github.py'
148--- charmworld/jobs/tests/test_github.py 2013-10-10 17:59:16 +0000
149+++ charmworld/jobs/tests/test_github.py 2013-11-18 15:35:18 +0000
150@@ -8,26 +8,29 @@
151 create_entries_from_pull_requests,
152 get_charm_repos,
153 get_pull_requests,
154+ GithubRateLimitError,
155 make_github_request,
156 )
157 from charmworld.testing import MongoTestBase
158
159
160 class FakeResponse:
161- def __init__(self, json={}):
162+ def __init__(self, json={}, headers={}):
163 self._json = json
164+ self.headers = headers
165
166 def json(self):
167 return self._json
168
169
170 class FakeGet:
171- def __init__(self, json=None):
172+ def __init__(self, json=None, headers={}):
173 self.json = json
174+ self.headers = headers
175
176 def __call__(self, url):
177 self.url = url
178- return FakeResponse(self.json)
179+ return FakeResponse(self.json, self.headers)
180
181
182 class GithubTest(MongoTestBase):
183@@ -47,6 +50,12 @@
184 with patch('requests.get', FakeGet()):
185 self.assertRaises(ValueError, make_github_request, 'pulls')
186
187+ def test_make_github_request_rate_limit_check(self):
188+ with patch(
189+ 'requests.get', FakeGet(headers={'x-ratelimit-remaining': 0})):
190+ self.assertRaises(
191+ GithubRateLimitError, make_github_request, 'repos')
192+
193 def test_get_charm_repos(self):
194 test_json = json.load(file('charmworld/testing/data/repos.json'))
195 with patch('requests.get', FakeGet(json=test_json)) as mock:

Subscribers

People subscribed via source and target branches

to all changes: