Merge lp:~om26er/core-snap/improve_build_script into lp:core-snap

Proposed by Omer Akram on 2016-10-04
Status: Needs review
Proposed branch: lp:~om26er/core-snap/improve_build_script
Merge into: lp:core-snap
Diff against target: 295 lines (+159/-122)
1 file modified
cron-scripts/lp-build-core (+159/-122)
To merge this branch: bzr merge lp:~om26er/core-snap/improve_build_script
Reviewer Review Type Date Requested Status
Snappy Developers 2016-10-04 Pending
Review via email: mp+307640@code.launchpad.net

Commit Message

* Port build script to python3
* Organize the code into a class
* Rename the script to lp-build-core
* Other cleanups

Description of the Change

While taking a look at the build script, I ended up making some changes to it. Below is what changed

* Port build script to python3
* Organize the code into a class
* Rename the script to lp-build-core
* Other cleanups

To post a comment you must log in.
49. By Omer Akram on 2016-10-05

Merge with trunk

Omer Akram (om26er) wrote :

Fixed conflicts, rebased on top of current trunk.

Unmerged revisions

49. By Omer Akram on 2016-10-05

Merge with trunk

48. By Omer Akram on 2016-10-04

rename build script to lp-build-core

47. By Omer Akram on 2016-10-04

Port the build script to python3, move the code into a class

46. By Omer Akram on 2016-10-04

join paths instead of concatination

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cron-scripts/lp-build-core'
2--- cron-scripts/lp-build-core 2016-10-05 10:33:07 +0000
3+++ cron-scripts/lp-build-core 2016-10-05 13:18:34 +0000
4@@ -1,132 +1,169 @@
5-#! /usr/bin/python
6+#!/usr/bin/python3
7
8 import os
9 import sys
10 import time
11 import smtplib
12-
13 from datetime import datetime
14-from os.path import basename
15+
16 from launchpadlib.launchpad import Launchpad
17 from email.mime.text import MIMEText
18
19-# where failure mails go
20-mailreceivers = ['ogra@ubuntu.com']
21-
22-# basic data
23-arches = ['amd64', 'i386', 'armhf', 'arm64', 'ppc64el', 's390x']
24-series = 'xenial'
25-
26-# basic paths
27-home = os.getenv("HOME")
28-workdir = home+"/core-builds"
29-
30+MAIL_RECEIVERS = ['ogra@ubuntu.com']
31+MAIL_SENDER = 'ogra@canonical.com'
32+ARCHES = ['amd64', 'i386', 'armhf', 'arm64', 'ppc64el', 's390x']
33+SERIES = 'xenial'
34+HOME = os.getenv("HOME")
35+WORK_DIR = os.path.join(HOME, "core-builds")
36+PID_FILE = os.path.join(WORK_DIR, "build.pid")
37 # we need to store credentials once for cronned builds
38-cachedir = workdir+"/cache"
39-creds = workdir+"/credentials"
40-
41-def sendMail(arch, stamp, failure, buildlog):
42- # send failure mails to defined recipients
43- sender = 'ogra@canonical.com'
44- if buildlog:
45- mailtext = "core {} snap\nBuild requested at {}\nFailed for id: {}\nLink to build log: {}".format(arch, stamp, failure, buildlog)
46- mailtext = "build-core: {} ".format(failure)
47-
48- message = MIMEText(mailtext)
49- message['Subject'] = '%s core snap failed to build !' % arch
50- message['From'] = 'Snap Auto Builder <%s>' % sender
51- message['To'] = ", ".join(mailreceivers)
52- try:
53- smtpObj = smtplib.SMTP('localhost')
54- smtpObj.sendmail(sender, mailreceivers, message.as_string())
55- print ("Successfully sent failure email to {}".format(mailreceivers))
56- except Exception:
57- print ("Error: unable to send failure email")
58-
59-
60-# make sure we dont clash with an older build attempt
61-pid = str(os.getpid())
62-pidfile = workdir+"/build.pid"
63-if os.path.isfile(pidfile):
64- print "A pid file %s already exists, it seems like" % pidfile
65- print "there is already a lp-build-core running."
66- print "exiting !"
67- sendMail("","","pid exists","")
68- sys.exit()
69-file(pidfile, 'w').write(pid)
70-
71-# log in
72-launchpad = Launchpad.login_with('Ubuntu Core Builds',
73- 'production', cachedir,
74- credentials_file=creds,
75- version='devel')
76-
77-# get snappy-dev team data and ppa
78-snappydev = launchpad.people['snappy-dev']
79-imageppa = snappydev.getPPAByName(name='image')
80-
81-# get snap
82-ubuntucore = launchpad.snaps.getByName(name='core',
83- owner=snappydev)
84-
85-# get distro info
86-ubuntu = launchpad.distributions['ubuntu']
87-release = ubuntu.getSeries(name_or_version=series)
88-
89-# print a stamp
90-stamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
91-print("Trying to trigger builds at: {}".format(stamp))
92-
93-# loop over arches and trigger builds
94-mybuilds = []
95-for buildarch in arches:
96- arch = release.getDistroArchSeries(archtag=buildarch)
97- request = ubuntucore.requestBuild(archive=imageppa,
98- distro_arch_series=arch,
99- pocket='Proposed')
100- buildid = str(request).rsplit('/', 1)[-1]
101- mybuilds.append(buildid)
102- print("Arch: {} is building under: {}".format(buildarch,
103- request))
104-
105-# check the status each minute til all builds have finished
106-failures = []
107-while len(mybuilds):
108- for build in mybuilds:
109- try:
110- response = ubuntucore.getBuildSummariesForSnapBuildIds(snap_build_ids=[build])
111- except:
112- print ("could not get response for {} (was there an LP timeout ?)".format(build))
113- continue
114- status = response[build]['status']
115- if status == "FULLYBUILT":
116- mybuilds.remove(build)
117- continue
118- elif status == "FAILEDTOBUILD":
119- failures.append(build)
120- mybuilds.remove(build)
121- continue
122- elif status == "CANCELLED":
123- mybuilds.remove(build)
124- continue
125- time.sleep(60)
126-
127-# if we had failures, print them and send mails
128-if len(failures):
129- for failure in failures:
130- try:
131- response = ubuntucore.getBuildSummariesForSnapBuildIds(snap_build_ids=[failure])
132- except:
133- print ("could not get failure data for {} (was there an LP timeout ?)".format(build))
134- continue
135- buildlog = response[build]['build_log_url']
136- if buildlog != 'None':
137- print(buildlog)
138- arch = str(buildlog).split('_')[4]
139- print("core snap {} build at {} failed for id: {} log: {}".format(arch, stamp, failure, buildlog))
140- sendMail(arch, stamp, failure, buildlog)
141-
142-
143-# tell the log we are done and remove pid file
144-print("done")
145-os.unlink(pidfile)
146+CACHE_DIR = os.path.join(WORK_DIR, "cache")
147+CREDENTIALS = os.path.join(WORK_DIR, "credentials")
148+
149+
150+class CoreBuilder:
151+ @classmethod
152+ def build(cls):
153+ cls._create_pid_lock()
154+ core, image_ppa, release = cls._fetch_data_from_launchpad()
155+ stamp = cls._print_and_get_timestamp()
156+ failures = cls._monitor_build_status(
157+ cls._trigger_builds(core, image_ppa, release),
158+ core
159+ )
160+ cls._report_failures_if_any(failures, core, stamp)
161+ # tell the log we are done and remove pid file
162+ print("done")
163+ os.unlink(PID_FILE)
164+
165+ @staticmethod
166+ def _create_pid_lock():
167+ # make sure we don't clash with an older build attempt
168+ if os.path.isfile(PID_FILE):
169+ print("A pid file {} already exists, it seems like".format(
170+ PID_FILE))
171+ print("there is already a build-core running.")
172+ print("exiting !")
173+ CoreBuilder._send_failure_mail("", "", "pid exists", "")
174+ sys.exit()
175+ with open(PID_FILE, 'w') as file:
176+ file.write(str(os.getpid()))
177+
178+ @staticmethod
179+ def _assemble_mail(arch, stamp, failure, build_log):
180+ if build_log:
181+ mail_text = "core {} snap\nBuild requested at {}\n" \
182+ "Failed for id: {}\nLink to build log: {}".format(
183+ arch, stamp, failure, build_log)
184+ else:
185+ mail_text = "build-core: {} ".format(failure)
186+
187+ message = MIMEText(mail_text)
188+ message['Subject'] = '{} core snap failed to build !'.format(arch)
189+ message['From'] = 'Snap Auto Builder <{}>'.format(MAIL_SENDER)
190+ message['To'] = ", ".join(MAIL_RECEIVERS)
191+ return message.as_string()
192+
193+ @staticmethod
194+ def _send_failure_mail(arch, stamp, failure, build_log):
195+ try:
196+ smtp_obj = smtplib.SMTP('localhost')
197+ smtp_obj.sendmail(
198+ MAIL_SENDER,
199+ MAIL_RECEIVERS,
200+ CoreBuilder._assemble_mail(arch, stamp, failure, build_log)
201+ )
202+ print(
203+ "Successfully sent failure email to {}".format(MAIL_RECEIVERS)
204+ )
205+ except Exception:
206+ print("Error: unable to send failure email")
207+
208+ @staticmethod
209+ def _fetch_data_from_launchpad():
210+ # log in
211+ launchpad = Launchpad.login_with(
212+ 'Ubuntu Core Builds',
213+ 'production',
214+ CACHE_DIR,
215+ credentials_file=CREDENTIALS,
216+ version='devel'
217+ )
218+ # get snappy-dev team data and ppa
219+ snappy_dev = launchpad.people['snappy-dev']
220+ image_ppa = snappy_dev.getPPAByName(name='image')
221+ # get snap
222+ core = launchpad.snaps.getByName(name='core', owner=snappy_dev)
223+ # get distro info
224+ ubuntu = launchpad.distributions['ubuntu']
225+ release = ubuntu.getSeries(name_or_version=SERIES)
226+ return core, image_ppa, release
227+
228+ @staticmethod
229+ def _print_and_get_timestamp():
230+ time_stamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
231+ print("Trying to trigger builds at: {}".format(time_stamp))
232+ return time_stamp
233+
234+ @staticmethod
235+ def _trigger_builds(core, image_ppa, release):
236+ my_builds = []
237+ for build_arch in ARCHES:
238+ arch = release.getDistroArchSeries(archtag=build_arch)
239+ request = core.requestBuild(
240+ archive=image_ppa, distro_arch_series=arch, pocket='Proposed')
241+ build_id = str(request).rsplit('/', 1)[-1]
242+ my_builds.append(build_id)
243+ print("Arch: {} is building under: {}".format(build_arch, request))
244+ return my_builds
245+
246+ @staticmethod
247+ def _monitor_build_status(my_builds, core):
248+ failures = []
249+ while len(my_builds):
250+ for build in my_builds:
251+ try:
252+ response = core.getBuildSummariesForSnapBuildIds(
253+ snap_build_ids=[build])
254+ except Exception:
255+ print(
256+ "could not get response for {} (was there an LP "
257+ "timeout ?)".format(build))
258+ continue
259+ status = response[build]['status']
260+ if status == "FULLYBUILT":
261+ my_builds.remove(build)
262+ continue
263+ elif status == "FAILEDTOBUILD":
264+ failures.append(build)
265+ my_builds.remove(build)
266+ continue
267+ elif status == "CANCELLED":
268+ my_builds.remove(build)
269+ continue
270+ time.sleep(60)
271+ return failures
272+
273+ @staticmethod
274+ def _report_failures_if_any(failures, core, stamp):
275+ for failure in failures:
276+ try:
277+ response = core.getBuildSummariesForSnapBuildIds(
278+ snap_build_ids=[failure])
279+ except Exception:
280+ print(
281+ "could not get failure data for {} (was there an LP "
282+ "timeout ?)".format(failure))
283+ continue
284+ build_log = response[failure]['build_log_url']
285+ if build_log != 'None':
286+ print(build_log)
287+ arch = str(build_log).split('_')[4]
288+ print(
289+ "core snap {} build at {} failed for id: {} log: "
290+ "{}".format(arch, stamp, failure, build_log))
291+ CoreBuilder._send_failure_mail(arch, stamp, failure, build_log)
292+
293+
294+if __name__ == "__main__":
295+ CoreBuilder.build()

Subscribers

People subscribed via source and target branches

to all changes: