Merge lp:~fginther/ubuntu-ci-services-itself/add-lp-build-logs into lp:ubuntu-ci-services-itself

Proposed by Francis Ginther
Status: Merged
Approved by: Chris Johnston
Approved revision: 373
Merged at revision: 375
Proposed branch: lp:~fginther/ubuntu-ci-services-itself/add-lp-build-logs
Merge into: lp:ubuntu-ci-services-itself
Diff against target: 284 lines (+102/-26)
5 files modified
branch-source-builder/bsbuilder/run_worker.py (+8/-4)
branch-source-builder/bsbuilder/upload_package.py (+8/-5)
branch-source-builder/watch_ppa.py (+56/-9)
lander/bin/lander_service_wrapper.py (+11/-4)
lander/bin/ticket_api.py (+19/-4)
To merge this branch: bzr merge lp:~fginther/ubuntu-ci-services-itself/add-lp-build-logs
Reviewer Review Type Date Requested Status
Chris Johnston (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Andy Doan (community) Approve
Review via email: mp+210668@code.launchpad.net

Commit message

Pass the completed subticket buildlogs to the lander and have it provide subticket status updates.

Description of the change

Pass the completed subticket buildlogs to the lander and have it provide subticket status updates.

The subticket status updates are already being passed back, the just turns on the lander functionality to update the ticket.

Here's a ticket with the subticket artifacts and progress state updates: http://15.125.114.120/ticket.html?ticket_id=6

While testing this, I ran into lp:1291591. I discovered that the check_ppa code which looks for prior uploads was running *after* the upload_source_packages method which does the dput to the ppa. So it's been pure timing luck that we don't see an upload failure on every ticket. I finally hit case where my new source package made it to the ppa before check_ppa executed.

The fix is fairly simply, split upload_packages.upload_source_packages() into two methods, one to crete the upload list, the other to do the dput and then call check_ppa() in between. This was a smaller fix, so I rolled it into here.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:372
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/397/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/397/rebuild

review: Needs Fixing (continuous-integration)
373. By Francis Ginther

Pep8 fix.

Revision history for this message
Andy Doan (doanac) wrote :

yeah!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:373
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/398/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/398/rebuild

review: Approve (continuous-integration)
Revision history for this message
Chris Johnston (cjohnston) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'branch-source-builder/bsbuilder/run_worker.py'
2--- branch-source-builder/bsbuilder/run_worker.py 2014-03-12 05:26:00 +0000
3+++ branch-source-builder/bsbuilder/run_worker.py 2014-03-13 03:31:31 +0000
4@@ -51,9 +51,9 @@
5 The return is an artifact entry for the progress message.'''
6 package_build_result_filename = 'package_build.output.log'
7 body = '{}\n\n'.format(content['message'])
8- body += 'Uploaded source packages:\n'
9+ body += 'Uploaded source packages:'
10 for subticket_status in content['subticket_status']:
11- body += 'name: {}\n'.format(subticket_status['name'])
12+ body += '\nname: {}\n'.format(subticket_status['name'])
13 body += 'version: {}\n'.format(subticket_status['version'])
14 body += 'status: {}\n'.format(subticket_status['status_text'])
15 if subticket_status.get('message'):
16@@ -88,7 +88,7 @@
17 'artifacts': [],
18 }
19
20- upload_list = upload_package.upload_source_packages(ppa, subtickets)
21+ upload_list = upload_package.create_upload_list(subtickets)
22 log.info('upload_list: {}'.format(upload_list))
23
24 if params.get('cancelled'):
25@@ -108,6 +108,9 @@
26 create_result_file(datastore, out_data))
27 amqp_utils.progress_failed(trigger, out_data)
28 else:
29+ # After checking the PPA, it's now safe to do the upload
30+ upload_package.upload_source_packages(ppa, upload_list)
31+
32 # Set ret to 1, the upload is failed until it succeeds
33 ret = 1
34 start_time = time.time()
35@@ -117,7 +120,8 @@
36 return amqp_utils.progress_failed, out_data
37
38 (ret, subticket_status) = watch_ppa.watch_ppa(
39- start_time, series, ppa, archive, None, upload_list)
40+ datastore, start_time, series, ppa, archive, None,
41+ upload_list)
42 log.info('subticket_status: {}'.format(subticket_status))
43 out_data['subticket_status'] = subticket_status
44 if ret == -1:
45
46=== modified file 'branch-source-builder/bsbuilder/upload_package.py'
47--- branch-source-builder/bsbuilder/upload_package.py 2014-03-10 22:11:05 +0000
48+++ branch-source-builder/bsbuilder/upload_package.py 2014-03-13 03:31:31 +0000
49@@ -119,11 +119,16 @@
50 'Not found: {}'.format(package_file))
51
52
53-def upload_source_packages(ppa, subtickets):
54+def create_upload_list(subtickets):
55+ '''Parses the subtickets into the upload list.'''
56+ upload_list = get_source_files(subtickets)
57+ parse_source_files(upload_list)
58+ return upload_list
59+
60+
61+def upload_source_packages(ppa, upload_list):
62 '''Attempts source file upload into the PPA.'''
63 logging.info('Upload to the ppa: {}'.format(ppa))
64- upload_list = get_source_files(subtickets)
65- parse_source_files(upload_list)
66 for package in upload_list:
67 packagemanager.upload_package(package['name'], package['version'],
68 ppa, package['location'])
69@@ -133,8 +138,6 @@
70 logging.info('Removing temp directory: {}'.format(directory))
71 shutil.rmtree(directory)
72
73- return upload_list
74-
75
76 def main():
77 logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s "
78
79=== modified file 'branch-source-builder/watch_ppa.py'
80--- branch-source-builder/watch_ppa.py 2014-03-11 15:43:21 +0000
81+++ branch-source-builder/watch_ppa.py 2014-03-13 03:31:31 +0000
82@@ -95,7 +95,50 @@
83 return launchpadmanager.get_ppa(ppa)
84
85
86-def create_package_status(package, step, status, message=None):
87+def get_launchpad_log(url):
88+ '''Retrieves the build log from launchpad (thanks to celso).'''
89+ if not url:
90+ return None
91+ api_url = url.replace('/launchpad.net/', '/api.launchpad.net/devel/')
92+ lp = launchpadmanager.get_launchpad()
93+ return lp._browser.get(api_url)
94+
95+
96+def get_build_logs(datastore, ppa, package):
97+ '''Constructs artifacts from the build logs for the given package.'''
98+ source_name = package.source_name
99+ version = package.version
100+
101+ collection = ppa.getPublishedSources(source_name=source_name,
102+ version=version)
103+ builds = collection[0].getBuilds()
104+
105+ artifacts = []
106+ for build in builds:
107+ build_log = get_launchpad_log(build.build_log_url)
108+ upload_log = get_launchpad_log(build.upload_log_url)
109+ if build_log:
110+ artifact_name = 'package_build.{}.{}.build.log'.format(
111+ source_name, build.arch_tag)
112+ location = datastore.put_file(artifact_name, build_log,
113+ 'text/plain')
114+ artifacts.append({
115+ 'name': artifact_name,
116+ 'reference': location,
117+ 'type': 'LOGS'})
118+ if upload_log:
119+ artifact_name = 'package_build.{}.{}.upload.log'.format(
120+ source_name, build.arch_tag)
121+ location = datastore.put_file(artifact_name, upload_log,
122+ 'text/plain')
123+ artifacts.append({
124+ 'name': artifact_name,
125+ 'reference': location,
126+ 'type': 'LOGS'})
127+ return artifacts
128+
129+
130+def create_package_status(package, step, status, message=None, artifacts=None):
131 return {
132 'name': package.source_name,
133 'version': package.version,
134@@ -104,11 +147,13 @@
135 'status': status.value,
136 'status_text': status.title,
137 'message': message,
138+ 'artifacts': artifacts,
139 }
140
141
142-def collect_subticket_status(ppa, packages_not_in_ppa, packages_building,
143- packages_failed, packages_complete):
144+def collect_subticket_status(datastore, ppa, packages_not_in_ppa,
145+ packages_building, packages_failed,
146+ packages_complete):
147 '''Collects the sets of package build status into a subticket_status.'''
148 subticket_status = []
149 for package in packages_not_in_ppa:
150@@ -125,13 +170,15 @@
151 subticket_status.append(create_package_status(
152 package, SubTicketWorkflowStep.COMPLETED,
153 SubTicketWorkflowStepStatus.PKG_BUILDING_FAILED,
154- FAILED_SOURCE_MESSAGE.format(ppa.web_link)))
155+ FAILED_SOURCE_MESSAGE.format(ppa.web_link),
156+ get_build_logs(datastore, ppa, package)))
157
158 for package in packages_complete:
159 subticket_status.append(create_package_status(
160 package, SubTicketWorkflowStep.COMPLETED,
161 SubTicketWorkflowStepStatus.PKG_BUILDING_COMPLETED,
162- PASSED_SOURCE_MESSAGE.format(ppa.web_link)))
163+ PASSED_SOURCE_MESSAGE.format(ppa.web_link),
164+ get_build_logs(datastore, ppa, package)))
165
166 return subticket_status
167
168@@ -206,7 +253,7 @@
169 return None
170
171
172-def watch_ppa(time_start, series, ppa, dest_ppa, arch, upload_list):
173+def watch_ppa(datastore, time_start, series, ppa, dest_ppa, arch, upload_list):
174 # Prepare launchpad connection:
175 lp_series = launchpadmanager.get_series(series)
176 monitored_ppa = launchpadmanager.get_ppa(ppa)
177@@ -264,9 +311,9 @@
178 packages_not_in_ppa, packages_building, packages_failed,
179 packages_complete, arch)
180
181- status = collect_subticket_status(monitored_ppa, packages_not_in_ppa,
182- packages_building, packages_failed,
183- packages_complete)
184+ status = collect_subticket_status(datastore, monitored_ppa,
185+ packages_not_in_ppa, packages_building,
186+ packages_failed, packages_complete)
187 for package in status:
188 subticket = find_subticket(upload_list, package['name'],
189 package['version'])
190
191=== modified file 'lander/bin/lander_service_wrapper.py'
192--- lander/bin/lander_service_wrapper.py 2014-03-11 19:33:29 +0000
193+++ lander/bin/lander_service_wrapper.py 2014-03-13 03:31:31 +0000
194@@ -49,14 +49,18 @@
195 class MessageSync(object):
196 progress = logging.getLogger('PROGRESS_TRIGGER')
197
198- def __init__(self, ticket, started_cb=None, completed_cb=None):
199+ def __init__(self, ticket, started_cb=None, completed_cb=None,
200+ message_cb=None):
201 if not started_cb:
202 started_cb = lambda: True
203 if not completed_cb:
204 completed_cb = lambda x: True
205+ if not message_cb:
206+ message_cb = lambda x: True
207 self.ticket = ticket
208 self.started_cb = started_cb
209 self.completed_cb = completed_cb
210+ self.message_cb = message_cb
211
212 def on_message(self, msg):
213 data = json.loads(msg.body)
214@@ -69,6 +73,8 @@
215 else:
216 self.progress.info(state)
217
218+ self.message_cb(data)
219+
220 if state == 'STATUS':
221 self.started_cb()
222 elif state == 'COMPLETED':
223@@ -86,8 +92,8 @@
224
225
226 def _drain_progress(amqp_config, queue, ticket, started_cb=None,
227- completed_cb=None):
228- sync = MessageSync(ticket, started_cb, completed_cb)
229+ completed_cb=None, message_cb=None):
230+ sync = MessageSync(ticket, started_cb, completed_cb, message_cb)
231 amqp_utils.process_queue(amqp_config, queue, sync.on_message, delete=True)
232 return sync.data
233
234@@ -195,7 +201,8 @@
235 logger.info('starting progress handler...')
236 return _drain_progress(args.amqp_config, queue, ticket,
237 ticket.set_pkg_inprogress,
238- ticket.set_pkg_complete)
239+ ticket.set_pkg_complete,
240+ ticket.set_subticket_progress)
241 except:
242 ticket.set_pkg_complete(False)
243 raise
244
245=== modified file 'lander/bin/ticket_api.py'
246--- lander/bin/ticket_api.py 2014-03-11 03:22:51 +0000
247+++ lander/bin/ticket_api.py 2014-03-13 03:31:31 +0000
248@@ -69,6 +69,19 @@
249 state = TicketWorkflowStepStatus.PKG_BUILDING_FAILED
250 self._patch_pkg_status(state)
251
252+ def _patch_subticket_status(self, subticket_id, step, status):
253+ url = self._url + '/updatesubticketstatus/' + str(subticket_id) + '/'
254+ self._patch(url, {'status': status, 'current_workflow_step': step})
255+
256+ def set_subticket_progress(self, data):
257+ '''Processes the content of messages with subticket_status.
258+
259+ Not all messages will have this content.'''
260+ for subticket in data.get('subticket_status', []):
261+ self._patch_subticket_status(subticket['id'], subticket['step'],
262+ subticket['status'])
263+ self.process_artifacts(subticket, subticket['id'])
264+
265 def _patch_ticket_status(self, status):
266 step = TicketWorkflowStep.IMAGE_BUILDING.value
267 status = status.value
268@@ -180,10 +193,12 @@
269 multiple updates. Attempts to create duplicate artifacts should be
270 caught when the artifact is placed in the data store by the creator.'''
271 current_artifacts = self.get_artifact_list(subticket_id)
272- for artifact in data.get('artifacts', []):
273- if artifact['name'] not in current_artifacts:
274- self.add_artifact(artifact['name'], artifact['reference'],
275- artifact['type'], subticket_id)
276+ artifact_list = data.get('artifacts', None)
277+ if artifact_list:
278+ for artifact in artifact_list:
279+ if artifact['name'] not in current_artifacts:
280+ self.add_artifact(artifact['name'], artifact['reference'],
281+ artifact['type'], subticket_id)
282
283 def get_full_ticket(self):
284 url = self._url + '/fullticket/' + self._ticket_id + '/'

Subscribers

People subscribed via source and target branches