]> git.ipfire.org Git - pbs.git/blame - src/web/builds.py
builds: Update API error response codes
[pbs.git] / src / web / builds.py
CommitLineData
f6e6ff79
MT
1#!/usr/bin/python
2
9cea0d62 3import errno
f6e6ff79
MT
4import tornado.web
5
f062b044 6from ..errors import NoSuchDistroError
d327b850 7from .. import misc
f062b044 8
0919f216 9from . import base
79a4d9b2 10from . import ui_modules
f6e6ff79 11
f062b044
MT
12class APIv1IndexHandler(base.APIMixin, base.BaseHandler):
13 # Allow users to create builds
14 allow_users = True
15
db90d0ea 16 @base.negotiate
f062b044
MT
17 async def post(self):
18 # Fetch the upload
9cea0d62 19 upload = self.get_argument_upload("upload")
f062b044
MT
20 if not upload:
21 raise tornado.web.HTTPError(404, "Could not find upload")
22
23 # Check permissions of the upload
24 if not upload.has_perm(self.current_user):
9cea0d62 25 raise base.APIError(errno.ENOPERM, "No permission for using upload %s" % upload)
f062b044
MT
26
27 # Fetch the repository
28 repo_name = self.get_argument("repo", None)
29
376abedf
MT
30 # Did the uploader request to disable test builds?
31 disable_test_builds = self.get_argument_bool("disable_test_builds")
32
f062b044
MT
33 with self.db.transaction():
34 # Import the package
35 try:
36 package = await self.backend.packages.create(upload)
37
38 # If the distribution that is coded into the package could not be found,
39 # we will send that error to the user...
40 except NoSuchDistroError as e:
9cea0d62 41 raise base.APIError(errno.ENOENT, "Could not find distribution: %s" % e)
f062b044 42
01f4459a
MT
43 try:
44 # Find the repository
45 repo = self.current_user.get_repo(package.distro, repo_name)
46 if not repo:
9cea0d62 47 raise base.APIError(errno.ENOENT, "Could not find repository")
01f4459a
MT
48
49 # Create a new build
50 build = await self.backend.builds.create(repo, package,
51 owner=self.current_user, disable_test_builds=disable_test_builds)
52
53 # If anything goes wrong, we will try to delete the package again
54 except Exception as e:
55 await package.delete(user=self.current_user)
56
57 raise e
f062b044
MT
58
59 # Delete the upload
60 await upload.delete()
61
97d1dea3
MT
62 # Launch the build
63 await self.backend.builds.launch([build])
64
f062b044
MT
65 # Send some data about the build
66 self.finish({
97d1dea3 67 "uuid" : "%s" % build.uuid,
f062b044
MT
68 "name" : "%s" % build,
69 })
70
f6e6ff79 71
75f80d94
MT
72class IndexHandler(base.BaseHandler):
73 def get(self):
20b9fe60
MT
74 # Pagination
75 offset = self.get_argument_int("offset", None) or 0
76 limit = self.get_argument_int("limit", None) or 25
77
c07aa62e 78 # Filters
a3e946d8 79 name = self.get_argument("name", None)
c07aa62e
MT
80 user = self.get_argument_user("user", None)
81
75f80d94 82 # Fetch the most recent builds
c07aa62e 83 if user:
a3e946d8 84 builds = user.get_builds(name, limit=limit, offset=offset)
c07aa62e 85 else:
a3e946d8 86 builds = self.backend.builds.get_recent(name=name, limit=limit, offset=offset)
75f80d94 87
d327b850
MT
88 # Group builds by date
89 builds = misc.group(builds, lambda build: build.created_at.date())
90
a3e946d8 91 self.render("builds/index.html", builds=builds, name=name, user=user,
20b9fe60 92 limit=limit, offset=offset)
75f80d94
MT
93
94
9c94f107 95class ShowHandler(base.BaseHandler):
6cfe6dc7 96 async def get(self, uuid):
f7bc5fb1
MT
97 build = self.backend.builds.get_by_uuid(uuid)
98 if not build:
99 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
f6e6ff79 100
6cfe6dc7
MT
101 # Fetch any fixed Bugs
102 bugs = await build.get_bugs()
f6e6ff79 103
c6ad77b9 104 self.render("builds/show.html", build=build, pkg=build.pkg,
ee20e731 105 distro=build.distro, bugs=bugs)
f6e6ff79
MT
106
107
0eecd2b5
MT
108class ApproveHandler(base.BaseHandler):
109 @tornado.web.authenticated
110 def get(self, uuid):
111 build = self.backend.builds.get_by_uuid(uuid)
112 if not build:
113 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
114
115 # Check if this can be approved at all
116 if not build.can_be_approved(self.current_user):
117 raise tornado.web.HTTPError(400)
118
119 self.render("builds/approve.html", build=build)
120
121 @tornado.web.authenticated
122 async def post(self, uuid):
123 build = self.backend.builds.get_by_uuid(uuid)
124 if not build:
125 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
126
127 # Check if this can be approved at all
128 if not build.can_be_approved(self.current_user):
129 raise tornado.web.HTTPError(400)
130
131 # Approve the build
132 with self.db.transaction():
133 await build.approve(self.current_user)
134
135 self.redirect("/builds/%s" % build.uuid)
136
137
9e2b040d
MT
138class CloneHandler(base.BaseHandler):
139 @tornado.web.authenticated
140 def get(self, uuid):
141 build = self.backend.builds.get_by_uuid(uuid)
142 if not build:
143 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
144
145 # Fetch repositories
146 try:
147 repos = self.current_user.repos[build.distro]
148 except KeyError:
149 repos = []
150
151 self.render("builds/clone.html", build=build, repos=repos)
152
153 @tornado.web.authenticated
154 async def post(self, uuid):
155 build = self.backend.builds.get_by_uuid(uuid)
156 if not build:
157 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
158
159 # Fetch the repository
160 repo = self.current_user.get_repo(build.distro, self.get_argument("repo"))
161
162 # Clone the build
163 with self.db.transaction():
164 clone = await self.backend.builds.create(
165 repo=repo, package=build.pkg, owner=self.current_user,
166 )
167
168 # Launch all jobs (in the background)
169 self.backend.run_task(self.backend.builds.launch, [clone])
170
171 self.redirect("/builds/%s" % clone.uuid)
172
173
93b7ce5f
MT
174class DeleteHandler(base.BaseHandler):
175 @tornado.web.authenticated
176 def get(self, uuid):
177 build = self.backend.builds.get_by_uuid(uuid)
178 if not build:
179 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
180
181 # Check permissions
182 if not build.can_be_deleted(self.current_user):
183 raise tornado.web.HTTPError(403, "%s cannot delete build %s" \
184 % (self.current_user, build))
185
186 self.render("builds/delete.html", build=build)
187
713e6fd2 188 @tornado.web.authenticated
64ae84e1 189 async def post(self, uuid):
713e6fd2
MT
190 build = self.backend.builds.get_by_uuid(uuid)
191 if not build:
192 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
193
93b7ce5f
MT
194 # Check permissions
195 if not build.can_be_deleted(self.current_user):
196 raise tornado.web.HTTPError(403, "%s cannot delete build %s" \
197 % (self.current_user, build))
198
199 # Perform Deletion
713e6fd2 200 with self.db.transaction():
64ae84e1 201 await build.delete(self.current_user)
713e6fd2 202
93b7ce5f 203 self.redirect("/builds")
713e6fd2
MT
204
205
93b7ce5f 206class WatchHandler(base.BaseHandler):
713e6fd2
MT
207 @tornado.web.authenticated
208 def post(self, uuid):
209 build = self.backend.builds.get_by_uuid(uuid)
210 if not build:
211 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
212
213 with self.db.transaction():
93b7ce5f 214 build.add_watcher(self.current_user)
713e6fd2
MT
215
216 self.redirect("/builds/%s" % build.uuid)
217
218
93b7ce5f 219class UnwatchHandler(base.BaseHandler):
f6e6ff79 220 @tornado.web.authenticated
93b7ce5f
MT
221 def post(self, uuid):
222 build = self.backend.builds.get_by_uuid(uuid)
223 if not build:
224 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
f6e6ff79 225
93b7ce5f
MT
226 with self.db.transaction():
227 build.remove_watcher(self.current_user)
f6e6ff79 228
93b7ce5f 229 self.redirect("/builds/%s" % build.uuid)
f6e6ff79
MT
230
231
fb548d37 232class CommentHandler(base.BaseHandler):
f6e6ff79 233 @tornado.web.authenticated
c99ba044 234 async def post(self, uuid):
47130fc1 235 build = self.backend.builds.get_by_uuid(uuid)
f6e6ff79 236 if not build:
fb548d37 237 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
f6e6ff79 238
fb548d37 239 text = self.get_argument("text")
f6e6ff79 240
fb548d37
MT
241 # Add a new comment to the build
242 with self.db.transaction():
c99ba044 243 await build.comment(self.current_user, text)
f6e6ff79 244
fb548d37
MT
245 # Redirect to the build
246 self.redirect("/builds/%s" % build.uuid)
f6e6ff79
MT
247
248
fd44f0a9
MT
249class BugHandler(base.BaseHandler):
250 @tornado.web.authenticated
251 async def get(self, uuid):
252 build = self.backend.builds.get_by_uuid(uuid)
253 if not build:
254 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
255
256 # Fetch fields
257 fields = await self.backend.bugzilla.fields
258
259 self.render("builds/bug.html", build=build, fields=fields)
260
261 @tornado.web.authenticated
262 async def post(self, uuid):
263 build = self.backend.builds.get_by_uuid(uuid)
264 if not build:
265 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
266
267 # Is the user connected to Bugzilla?
268 if not self.current_user.bugzilla:
269 raise tornado.web.HTTPError(400, "%s is not connected to Bugzilla" \
270 % self.current_user)
271
272 kwargs = {
273 # Summary & Description
274 "summary" : self.get_argument("summary"),
275 "description" : self.get_argument("description", None),
276 } | build.bugzilla_fields
277
278 # Create the bug
279 bug = await self.current_user.bugzilla.create_bug(**kwargs)
280
281 # Send the attachments
282 for job in build.jobs:
283 if not self.get_argument_bool("attach_log_%s" % job.uuid):
284 continue
285
286 # Open the logfile
287 try:
288 log = await job.open_log()
289 except FileNotFoundError as e:
290 log.warning("Could not open log file for %s" % job)
291 continue
292
293 # Attach it to the bug
294 await bug.attach(summary="Log file for %s" % job, filename="%s.log" % job,
295 data=log, content_type="text/plain")
296
297 self.render("builds/bug-created.html", build=build, bug=bug)
298
299
5ff12a44
MT
300class ReposAddHandler(base.BaseHandler):
301 @tornado.web.authenticated
302 def get(self, uuid):
303 build = self.backend.builds.get_by_uuid(uuid)
304 if not build:
305 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
306
307 # Fetch all available repositories
308 try:
309 repos = self.current_user.repos[build.distro]
310 except KeyError:
311 repos = None
312
313 self.render("builds/repos/add.html", build=build, repos=repos)
314
315 @tornado.web.authenticated
316 async def post(self, uuid):
317 build = self.backend.builds.get_by_uuid(uuid)
318 if not build:
319 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
320
321 slug = self.get_argument("repo")
322
323 # Fetch the repository
324 repo = self.current_user.get_repo(build.distro, slug)
325 if not repo:
326 raise tornado.web.HTTPError(400, "Could not find repository '%s'" % slug)
327
328 # Add the build to the repository
329 with self.db.transaction():
9444c75e 330 await repo.add_build(build, user=self.current_user)
5ff12a44
MT
331
332 self.redirect("/builds/%s" % build.uuid)
333
334
4d27c302
MT
335class ReposRemoveHandler(base.BaseHandler):
336 @tornado.web.authenticated
337 def get(self, uuid):
338 build = self.backend.builds.get_by_uuid(uuid)
339 if not build:
340 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
341
342 # Raise error when the build is in to repositories
343 if not build.repos:
344 raise tornado.web.HTTPError(400)
345
346 self.render("builds/repos/remove.html", build=build)
347
348 @tornado.web.authenticated
349 async def post(self, uuid):
350 build = self.backend.builds.get_by_uuid(uuid)
351 if not build:
352 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
353
354 # Fetch all selected repos
355 repos = self.get_arguments("repo")
356
357 # Raise an error if nothing has been selected
358 if not repos:
359 raise tornado.web.HTTPError(400, "No repositories selected")
360
361 # Find all selected repositories
362 repos = [repo for repo in build.repos if repo.slug in repos]
363
364 # Remove build from all repositories
365 with self.db.transaction():
bbb4cc87 366 for repo in repos:
9444c75e 367 await repo.remove_build(build, user=self.current_user)
4d27c302
MT
368
369 self.redirect("/builds/%s" % build.uuid)
370
371
ccae1b76
MT
372class GroupShowHandler(base.BaseHandler):
373 def get(self, uuid):
374 group = self.backend.builds.groups.get_by_uuid(uuid)
375 if not group:
376 raise tornado.web.HTTPError(404, "Could not find build group %s" % uuid)
377
378 self.render("builds/groups/show.html", group=group)
379
380
79a4d9b2 381class ListModule(ui_modules.UIModule):
a9d87299 382 def render(self, builds, limit=None, shorter=False, more_url=None):
a3e946d8
MT
383 rest = None
384
385 # Limit builds
386 if limit:
387 builds, rest = builds[:limit], builds[limit:]
388
389 return self.render_string("builds/modules/list.html", builds=builds,
a9d87299 390 rest=rest, shorter=shorter, more_url=more_url)
548cc46a
MT
391
392
393class GroupListModule(ui_modules.UIModule):
394 def render(self, group, limit=None):
395 return self.render_string("builds/groups/modules/list.html",
396 group=group, limit=limit)
d542b33c
MT
397
398
399class WatchersModule(ui_modules.UIModule):
400 def render(self, build, watchers=None):
401 if watchers is None:
402 watchers = build.watchers
403
404 return self.render_string("builds/modules/watchers.html",
405 build=build, watchers=watchers)