]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/hub/handlers.py
Migrate arch to text in packages table
[people/jschlag/pbs.git] / src / hub / handlers.py
CommitLineData
f6e6ff79
MT
1#!/usr/bin/python
2
3import base64
4import hashlib
c2902b29 5import json
f6e6ff79 6import logging
c2902b29 7import time
f6e6ff79 8import tornado.web
f6e6ff79 9
2c909128
MT
10from .. import builds
11from .. import builders
12from .. import uploads
13from .. import users
14
c2902b29
MT
15class LongPollMixin(object):
16 def initialize(self):
17 self._start_time = time.time()
f6e6ff79 18
c2902b29
MT
19 def add_timeout(self, timeout, callback):
20 deadline = time.time() + timeout
f6e6ff79 21
c2902b29 22 return self.application.ioloop.add_timeout(deadline, callback)
f6e6ff79 23
c2902b29
MT
24 def on_connection_close(self):
25 logging.debug("Connection closed unexpectedly")
f6e6ff79 26
c2902b29
MT
27 def connection_closed(self):
28 return self.request.connection.stream.closed()
f6e6ff79 29
c2902b29
MT
30 @property
31 def runtime(self):
32 return time.time() - self._start_time
f6e6ff79 33
f6e6ff79 34
c2902b29
MT
35class BaseHandler(LongPollMixin, tornado.web.RequestHandler):
36 @property
37 def backend(self):
f6e6ff79 38 """
4b9167ef 39 Shortcut handler to pakfire instance
f6e6ff79 40 """
4b9167ef 41 return self.application.backend
f6e6ff79 42
c2902b29 43 def get_basic_auth_credentials(self):
f6e6ff79
MT
44 """
45 This handles HTTP Basic authentication.
46 """
47 auth_header = self.request.headers.get("Authorization", None)
48
49 # If no authentication information was provided, we stop here.
50 if not auth_header:
c2902b29 51 return None, None
f6e6ff79
MT
52
53 # No basic auth? We cannot handle that.
54 if not auth_header.startswith("Basic "):
55 raise tornado.web.HTTPError(400, "Can only handle Basic auth.")
56
f6e6ff79 57 try:
c2902b29
MT
58 # Decode the authentication information.
59 auth_header = base64.decodestring(auth_header[6:])
60
f6e6ff79
MT
61 name, password = auth_header.split(":", 1)
62 except:
63 raise tornado.web.HTTPError(400, "Authorization data was malformed")
64
c2902b29 65 return name, password
f6e6ff79 66
c2902b29
MT
67 def get_current_user(self):
68 name, password = self.get_basic_auth_credentials()
69 if name is None:
70 return
f6e6ff79 71
c2902b29
MT
72 builder = self.backend.builders.auth(name, password)
73 if builder:
74 return builder
f6e6ff79 75
c2902b29
MT
76 user = self.backend.users.auth(name, password)
77 if user:
78 return user
f6e6ff79
MT
79
80 @property
81 def builder(self):
2c909128 82 if isinstance(self.current_user, builders.Builder):
c2902b29 83 return self.current_user
f6e6ff79 84
c2902b29
MT
85 @property
86 def user(self):
2c909128 87 if isinstance(self.current_user, users.User):
c2902b29 88 return self.current_user
f6e6ff79 89
c2902b29
MT
90 def get_argument_int(self, *args, **kwargs):
91 arg = self.get_argument(*args, **kwargs)
f6e6ff79 92
c2902b29
MT
93 try:
94 return int(arg)
95 except (TypeError, ValueError):
96 return None
f6e6ff79 97
c2902b29
MT
98 def get_argument_float(self, *args, **kwargs):
99 arg = self.get_argument(*args, **kwargs)
f6e6ff79 100
c2902b29
MT
101 try:
102 return float(arg)
103 except (TypeError, ValueError):
104 return None
f6e6ff79 105
c2902b29
MT
106 def get_argument_json(self, *args, **kwargs):
107 arg = self.get_argument(*args, **kwargs)
f6e6ff79 108
c2902b29
MT
109 if arg:
110 return json.loads(arg)
f6e6ff79 111
c2902b29
MT
112
113class NoopHandler(BaseHandler):
114 def get(self):
115 if self.builder:
116 self.write("Welcome to the Pakfire hub, %s!" % self.builder.hostname)
117 elif self.user:
118 self.write("Welcome to the Pakfire hub, %s!" % self.user.name)
f6e6ff79 119 else:
c2902b29 120 self.write("Welcome to the Pakfire hub!")
f6e6ff79 121
f6e6ff79 122
c2902b29
MT
123class ErrorTestHandler(BaseHandler):
124 def get(self, error_code=200):
125 """
126 For testing a client.
f6e6ff79 127
c2902b29
MT
128 This just returns a HTTP response with the given code.
129 """
130 try:
131 error_code = int(error_code)
132 except ValueError:
133 error_code = 200
f6e6ff79 134
c2902b29 135 raise tornado.web.HTTPError(error_code)
f6e6ff79 136
f6e6ff79 137
6efed544
MT
138class StatsBuildsTypesHandler(BaseHandler):
139 def get(self):
140 ret = {}
141
142 types = self.backend.builds.get_types_stats()
143 for type, count in types.items():
144 ret["builds_type_%s" % type] = count
145
146 self.write(ret)
147
148
149class StatsJobsHandler(BaseHandler):
150 def get(self):
151 ret = {}
152
153 self.write(ret)
154
155
156class StatsJobsDurationsHandler(BaseHandler):
157 def get(self):
158 durations = self.backend.jobs.get_build_durations()
159
160 ret = {}
161 for platform, d in durations.items():
162 ret.update({
163 "jobs_durations_%s_minimum" % platform : d.get("minimum", None),
164 "jobs_durations_%s_maximum" % platform : d.get("maximum", None),
165 "jobs_durations_%s_average" % platform : d.get("average", None),
166 "jobs_durations_%s_stddev" % platform : d.get("stddev", None),
167 })
168
169 self.write(ret)
170
171class StatsJobsStatesHandler(BaseHandler):
172 def get(self):
173 ret = {}
174
175 states = self.backend.jobs.get_state_stats()
176 for state, count in states.items():
177 ret["jobs_state_%s" % state] = count
178
179 self.write(ret)
180
181
c2902b29
MT
182class StatsJobsQueueHandler(BaseHandler):
183 def get(self):
6efed544
MT
184 ret = {}
185
186 # Queue length(s).
fd43d5e1 187 ret["job_queue_length"] = len(self.backend.jobqueue)
6efed544
MT
188
189 # Average waiting time.
fd43d5e1 190 ret["job_queue_avg_wait_time"] = self.backend.jobqueue.average_waiting_time
f6e6ff79 191
c2902b29 192 self.write(ret)
f6e6ff79 193
f6e6ff79 194
c2902b29 195# Uploads
f6e6ff79 196
c2902b29
MT
197class UploadsCreateHandler(BaseHandler):
198 """
199 Create a new upload object in the database and return a unique ID
200 to the uploader.
201 """
f6e6ff79
MT
202
203 @tornado.web.authenticated
c2902b29
MT
204 def get(self):
205 # XXX Check permissions
206
207 filename = self.get_argument("filename")
208 filesize = self.get_argument_int("filesize")
209 filehash = self.get_argument("hash")
210
2c909128 211 upload = uploads.Upload.create(self.backend, filename, filesize,
c2902b29 212 filehash, user=self.user, builder=self.builder)
f6e6ff79 213
c2902b29 214 self.finish(upload.uuid)
f6e6ff79 215
c2902b29
MT
216
217class UploadsSendChunkHandler(BaseHandler):
f6e6ff79 218 @tornado.web.authenticated
c2902b29
MT
219 def post(self, upload_id):
220 upload = self.backend.uploads.get_by_uuid(upload_id)
f6e6ff79
MT
221 if not upload:
222 raise tornado.web.HTTPError(404, "Invalid upload id.")
223
224 if not upload.builder == self.builder:
225 raise tornado.web.HTTPError(403, "Uploading an other host's file.")
226
c2902b29
MT
227 chksum = self.get_argument("chksum")
228 data = self.get_argument("data")
229
230 # Decode data.
231 data = base64.b64decode(data)
232
233 # Calculate hash and compare.
234 h = hashlib.new("sha512")
235 h.update(data)
236
237 if not chksum == h.hexdigest():
238 raise tornado.web.HTTPError(400, "Checksum mismatch")
239
240 # Append the data to file.
241 upload.append(data)
242
f6e6ff79 243
c2902b29 244class UploadsFinishedHandler(BaseHandler):
f6e6ff79 245 @tornado.web.authenticated
c2902b29
MT
246 def get(self, upload_id):
247 upload = self.backend.uploads.get_by_uuid(upload_id)
f6e6ff79
MT
248 if not upload:
249 raise tornado.web.HTTPError(404, "Invalid upload id.")
250
251 if not upload.builder == self.builder:
252 raise tornado.web.HTTPError(403, "Uploading an other host's file.")
253
254 # Validate the uploaded data to its hash.
255 ret = upload.validate()
256
257 # If the validation was successfull, we mark the upload
258 # as finished and send True to the client.
259 if ret:
260 upload.finished()
c2902b29
MT
261 self.finish("OK")
262
263 return
f6e6ff79
MT
264
265 # In case the download was corrupted or incomplete, we delete it
266 # and tell the client to start over.
267 upload.remove()
f6e6ff79 268
c2902b29
MT
269 self.finish("ERROR: CORRUPTED OR INCOMPLETE FILE")
270
271
272class UploadsDestroyHandler(BaseHandler):
f6e6ff79 273 @tornado.web.authenticated
c2902b29
MT
274 def get(self, upload_id):
275 upload = self.backend.uploads.get_by_uuid(upload_id)
f6e6ff79
MT
276 if not upload:
277 raise tornado.web.HTTPError(404, "Invalid upload id.")
278
279 if not upload.builder == self.builder:
280 raise tornado.web.HTTPError(403, "Removing an other host's file.")
281
282 # Remove the upload from the database and trash the data.
283 upload.remove()
284
285
c2902b29 286# Builds
f6e6ff79 287
c2902b29
MT
288class BuildsCreateHandler(BaseHandler):
289 @tornado.web.authenticated
290 def get(self):
291 # Get the upload ID of the package file.
292 upload_id = self.get_argument("upload_id")
f6e6ff79 293
c2902b29
MT
294 # Get the identifier of the distribution we build for.
295 distro_ident = self.get_argument("distro")
f6e6ff79 296
c2902b29
MT
297 # Get a list of arches to build for.
298 arches = self.get_argument("arches", None)
299 if arches == "":
300 arches = None
f6e6ff79 301
c2902b29
MT
302 # Process build type.
303 build_type = self.get_argument("build_type")
304 if build_type == "release":
305 check_for_duplicates = True
306 elif build_type == "scratch":
307 check_for_duplicates = False
308 else:
309 raise tornado.web.HTTPError(400, "Invalid build type")
f6e6ff79 310
c2902b29
MT
311 ## Check if the user has permission to create a build.
312 # Users only have the permission to create scratch builds.
313 if self.user and not build_type == "scratch":
314 raise tornado.web.HTTPError(403, "Users are only allowed to upload scratch builds")
f6e6ff79 315
c2902b29
MT
316 # Get previously uploaded file to create this build from.
317 upload = self.backend.uploads.get_by_uuid(upload_id)
318 if not upload:
319 raise tornado.web.HTTPError(400, "Upload does not exist: %s" % upload_id)
f6e6ff79 320
c2902b29
MT
321 # Check if the uploaded file belongs to this user/builder.
322 if self.user and not upload.user == self.user:
323 raise tornado.web.HTTPError(400, "Upload does not belong to this user.")
f6e6ff79 324
c2902b29
MT
325 elif self.builder and not upload.builder == self.builder:
326 raise tornado.web.HTTPError(400, "Upload does not belong to this builder.")
f6e6ff79 327
c2902b29
MT
328 # Get distribution this package should be built for.
329 distro = self.backend.distros.get_by_ident(distro_ident)
330 if not distro:
331 distro = self.backend.distros.get_default()
f6e6ff79 332
c2902b29
MT
333 # Open the package that was uploaded earlier and add it to
334 # the database. Create a new build object from the uploaded package.
335 args = {
336 "arches" : arches,
337 "check_for_duplicates" : check_for_duplicates,
338 "distro" : distro,
339 "type" : build_type,
340 }
341 if self.user:
342 args["owner"] = self.user
f6e6ff79 343
c2902b29 344 try:
2c909128 345 pkg, build = builds.import_from_package(self.backend, upload.path, **args)
f6e6ff79 346
c2902b29
MT
347 except:
348 # Raise any exception.
349 raise
f6e6ff79 350
c2902b29
MT
351 else:
352 # Creating the build will move the file to the build directory,
353 # so we can safely remove the uploaded file.
354 upload.remove()
f6e6ff79 355
c2902b29
MT
356 # Send the build ID back to the user.
357 self.finish(build.uuid)
f6e6ff79 358
f6e6ff79 359
c2902b29
MT
360class BuildsGetHandler(BaseHandler):
361 def get(self, build_uuid):
362 build = self.backend.builds.get_by_uuid(build_uuid)
363 if not build:
364 raise tornado.web.HTTPError(404, "Could not find build: %s" % build_uuid)
f6e6ff79 365
c2902b29
MT
366 ret = {
367 "distro" : build.distro.identifier,
368 "jobs" : [j.uuid for j in build.jobs],
369 "name" : build.name,
370 "package" : build.pkg.uuid,
371 "priority" : build.priority,
f6e6ff79 372 "score" : build.credits,
c2902b29
MT
373 "severity" : build.severity,
374 "state" : build.state,
375 "sup_arches" : build.supported_arches,
376 "time_created" : build.created.isoformat(),
377 "type" : build.type,
378 "uuid" : build.uuid,
f6e6ff79
MT
379 }
380
381 # If the build is in a repository, update that bit.
382 if build.repo:
c2902b29 383 ret["repo"] = build.repo.identifier
f6e6ff79 384
c2902b29 385 self.finish(ret)
f6e6ff79 386
f6e6ff79 387
c2902b29 388# Jobs
f6e6ff79 389
ba9f9092
MT
390class JobsBaseHandler(BaseHandler):
391 def job2json(self, job):
f6e6ff79 392 ret = {
22b715d7 393 "arch" : job.arch,
c2902b29 394 "build" : job.build.uuid,
c2902b29
MT
395 "duration" : job.duration,
396 "name" : job.name,
397 "packages" : [p.uuid for p in job.packages],
398 "state" : job.state,
399 "time_created" : job.time_created.isoformat(),
400 "type" : job.type,
401 "uuid" : job.uuid,
f6e6ff79
MT
402 }
403
ba9f9092
MT
404 if job.builder:
405 ret["builder"] = job.builder.hostname
406
c2902b29
MT
407 if job.time_started:
408 ret["time_started"] = job.time_started.isoformat()
f6e6ff79 409
c2902b29
MT
410 if job.time_finished:
411 ret["time_finished"] = job.time_finished.isoformat()
f6e6ff79 412
ba9f9092
MT
413 return ret
414
415
416class JobsGetActiveHandler(JobsBaseHandler):
417 def get(self):
418 # Get list of all active jobs.
419 jobs = self.backend.jobs.get_active()
420
421 args = {
422 "jobs" : [self.job2json(j) for j in jobs],
423 }
424
425 self.finish(args)
426
427
428class JobsGetLatestHandler(JobsBaseHandler):
429 def get(self):
430 limit = self.get_argument_int("limit", 5)
431
432 # Get the latest jobs.
433 jobs = self.backend.jobs.get_latest(age="24 HOUR", limit=limit)
434
435 args = {
436 "jobs" : [self.job2json(j) for j in jobs],
437 }
438
439 self.finish(args)
440
441
442class JobsGetQueueHandler(JobsBaseHandler):
443 def get(self):
444 limit = self.get_argument_int("limit", 5)
445
446 # Get the job queue.
fd43d5e1
MT
447 jobs = []
448 for job in self.backend.jobqueue:
449 jobs.append(job)
450
451 limit -= 1
452 if not limit: break
ba9f9092
MT
453
454 args = {
455 "jobs" : [self.job2json(j) for j in jobs],
456 }
457
458 self.finish(args)
459
460
461class JobsGetHandler(JobsBaseHandler):
462 def get(self, job_uuid):
463 job = self.backend.jobs.get_by_uuid(job_uuid)
464 if not job:
465 raise tornado.web.HTTPError(404, "Could not find job: %s" % job_uuid)
466
467 # Check if user is allowed to view this job.
468 if job.build.public == False:
469 if not self.user:
470 raise tornado.web.HTTPError(401)
471
472 # Check if an authenticated user has permission to see this build.
473 if not job.build.has_perm(self.user):
474 raise tornado.web.HTTPError(403)
475
476 ret = self.job2json(job)
c2902b29 477 self.finish(ret)
f6e6ff79 478
f6e6ff79 479
c2902b29 480# Packages
f6e6ff79 481
c2902b29
MT
482class PackagesGetHandler(BaseHandler):
483 def get(self, package_uuid):
484 pkg = self.backend.packages.get_by_uuid(package_uuid)
f6e6ff79 485 if not pkg:
c2902b29 486 raise tornado.web.HTTPError(404, "Could not find package: %s" % package_uuid)
f6e6ff79
MT
487
488 ret = {
22b715d7 489 "arch" : pkg.arch,
c2902b29
MT
490 "build_id" : pkg.build_id,
491 "build_host" : pkg.build_host,
492 "build_time" : pkg.build_time.isoformat(),
493 "description" : pkg.description,
494 "epoch" : pkg.epoch,
495 "filesize" : pkg.filesize,
f6e6ff79
MT
496 "friendly_name" : pkg.friendly_name,
497 "friendly_version" : pkg.friendly_version,
498 "groups" : pkg.groups,
c2902b29 499 "hash_sha512" : pkg.hash_sha512,
f6e6ff79 500 "license" : pkg.license,
c2902b29
MT
501 "name" : pkg.name,
502 "release" : pkg.release,
f6e6ff79 503 "size" : pkg.size,
c2902b29
MT
504 "summary" : pkg.summary,
505 "type" : pkg.type,
506 "url" : pkg.url,
507 "uuid" : pkg.uuid,
508 "version" : pkg.version,
f6e6ff79
MT
509
510 # Dependencies.
511 "prerequires" : pkg.prerequires,
512 "requires" : pkg.requires,
513 "provides" : pkg.provides,
514 "obsoletes" : pkg.obsoletes,
515 "conflicts" : pkg.conflicts,
f6e6ff79
MT
516 }
517
c2902b29
MT
518 if pkg.type == "source":
519 ret["supported_arches"] = pkg.supported_arches
520
2c909128 521 if isinstance(pkg.maintainer, users.User):
f6e6ff79
MT
522 ret["maintainer"] = "%s <%s>" % (pkg.maintainer.realname, pkg.maintainer.email)
523 elif pkg.maintainer:
524 ret["maintainer"] = pkg.maintainer
525
526 if pkg.distro:
c2902b29 527 ret["distro"] = pkg.distro.identifier
f6e6ff79 528
c2902b29 529 self.finish(ret)
f6e6ff79
MT
530
531
c2902b29 532# Builders
f6e6ff79 533
c2902b29
MT
534class BuildersBaseHandler(BaseHandler):
535 def prepare(self):
536 # The request must come from an authenticated buider.
537 if not self.builder:
538 raise tornado.web.HTTPError(403)
f6e6ff79 539
f6e6ff79 540
c2902b29 541class BuildersInfoHandler(BuildersBaseHandler):
f6e6ff79 542 @tornado.web.authenticated
c2902b29
MT
543 def post(self):
544 args = {
545 # CPU info
546 "cpu_model" : self.get_argument("cpu_model", None),
547 "cpu_count" : self.get_argument("cpu_count", None),
548 "cpu_arch" : self.get_argument("cpu_arch", None),
549 "cpu_bogomips" : self.get_argument("cpu_bogomips", None),
550
551 # Pakfire
552 "pakfire_version" : self.get_argument("pakfire_version", None),
553 "host_key" : self.get_argument("host_key", None),
554
555 # OS
556 "os_name" : self.get_argument("os_name", None),
557 }
558 self.builder.update_info(**args)
f6e6ff79 559
f6e6ff79 560
c2902b29
MT
561class BuildersKeepaliveHandler(BuildersBaseHandler):
562 @tornado.web.authenticated
563 def post(self):
564 args = {
565 # Load average
566 "loadavg1" : self.get_argument_float("loadavg1", None),
567 "loadavg5" : self.get_argument_float("loadavg5", None),
568 "loadavg15" : self.get_argument_float("loadavg15", None),
569
570 # Memory
571 "mem_total" : self.get_argument_int("mem_total", None),
572 "mem_free" : self.get_argument_int("mem_free", None),
573
574 # swap
575 "swap_total" : self.get_argument_int("swap_total", None),
576 "swap_free" : self.get_argument_int("swap_free", None),
577
578 # Disk space
579 "space_free" : self.get_argument_int("space_free", None),
580 }
581 self.builder.update_keepalive(**args)
f6e6ff79 582
c2902b29 583 self.finish("OK")
f6e6ff79 584
f6e6ff79 585
c2902b29
MT
586class BuildersJobsQueueHandler(BuildersBaseHandler):
587 @tornado.web.asynchronous
588 @tornado.web.authenticated
589 def get(self):
590 self.callback()
f6e6ff79 591
c2902b29
MT
592 def callback(self):
593 # Break if the connection has been closed in the mean time.
594 if self.connection_closed():
595 logging.warning("Connection closed")
f6e6ff79
MT
596 return
597
c2902b29
MT
598 # Check if there is a job for us.
599 job = self.builder.get_next_job()
600
601 # Got no job, wait and try again.
f6e6ff79 602 if not job:
c2902b29
MT
603 # Check if we have been running for too long.
604 if self.runtime >= self.max_runtime:
605 logging.debug("Exceeded max. runtime. Finishing request.")
606 return self.finish()
f6e6ff79 607
c2902b29
MT
608 # Try again in a jiffy.
609 self.add_timeout(self.heartbeat, self.callback)
610 return
f6e6ff79
MT
611
612 try:
613 # Set job to dispatching state.
614 job.state = "dispatching"
615
616 # Set our build host.
617 job.builder = self.builder
618
619 ret = {
620 "id" : job.uuid,
22b715d7 621 "arch" : job.arch,
c2902b29
MT
622 "source_url" : job.build.source_download,
623 "source_hash_sha512" : job.build.source_hash_sha512,
f6e6ff79
MT
624 "type" : job.type,
625 "config" : job.get_config(),
626 }
627
628 # Send build information to the builder.
c2902b29 629 self.finish(ret)
f6e6ff79
MT
630 except:
631 # If anything went wrong, we reset the state.
632 job.state = "pending"
633 raise
634
c2902b29
MT
635 @property
636 def heartbeat(self):
637 return 15 # 15 seconds
638
639 @property
640 def max_runtime(self):
641 timeout = self.get_argument_int("timeout", None)
642 if timeout:
643 return timeout - self.heartbeat
644
645 return 300 # 5 min
646
647
648class BuildersJobsStateHandler(BuildersBaseHandler):
649 @tornado.web.authenticated
650 def post(self, job_uuid, state):
651 job = self.backend.jobs.get_by_uuid(job_uuid)
f6e6ff79
MT
652 if not job:
653 raise tornado.web.HTTPError(404, "Invalid job id.")
654
655 if not job.builder == self.builder:
656 raise tornado.web.HTTPError(403, "Altering another builder's build.")
657
658 # Save information to database.
659 job.state = state
c2902b29
MT
660
661 message = self.get_argument("message", None)
f6e6ff79
MT
662 job.update_message(message)
663
c2902b29
MT
664 self.finish("OK")
665
666
667class BuildersJobsBuildrootHandler(BuildersBaseHandler):
668 @tornado.web.authenticated
669 def post(self, job_uuid):
670 job = self.backend.jobs.get_by_uuid(job_uuid)
671 if not job:
672 raise tornado.web.HTTPError(404, "Invalid job id.")
673
674 if not job.builder == self.builder:
675 raise tornado.web.HTTPError(403, "Altering another builder's build.")
f6e6ff79 676
c2902b29
MT
677 # Get buildroot.
678 buildroot = self.get_argument_json("buildroot", None)
679 if buildroot:
680 job.save_buildroot(buildroot)
681
682 self.finish("OK")
683
684
685class BuildersJobsAddFileHandler(BuildersBaseHandler):
686 @tornado.web.authenticated
687 def post(self, job_uuid, upload_id):
688 type = self.get_argument("type")
f6e6ff79
MT
689 assert type in ("package", "log")
690
691 # Fetch job we are working on and check if it is actually ours.
c2902b29 692 job = self.backend.jobs.get_by_uuid(job_uuid)
f6e6ff79
MT
693 if not job:
694 raise tornado.web.HTTPError(404, "Invalid job id.")
695
696 if not job.builder == self.builder:
697 raise tornado.web.HTTPError(403, "Altering another builder's job.")
698
699 # Fetch uploaded file object and check we uploaded it ourself.
c2902b29 700 upload = self.backend.uploads.get_by_uuid(upload_id)
f6e6ff79
MT
701 if not upload:
702 raise tornado.web.HTTPError(404, "Invalid upload id.")
703
704 if not upload.builder == self.builder:
705 raise tornado.web.HTTPError(403, "Using an other host's file.")
706
707 # Remove all files that have to be deleted, first.
c2902b29 708 self.backend.cleanup_files()
f6e6ff79
MT
709
710 try:
711 job.add_file(upload.path)
712
713 finally:
714 # Finally, remove the uploaded file.
715 upload.remove()
716
c2902b29 717 self.finish("OK")