OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import datetime |
| 6 import json |
| 7 |
| 8 import webapp2 |
| 9 from google.appengine.ext import ndb |
| 10 |
| 11 |
| 12 DEFAULT_NAMESPACE = 'default' |
| 13 DEFAULT_JOBS_TO_SERVE = 20 |
| 14 |
| 15 |
| 16 class Job(ndb.Model): |
| 17 """Represents a single build job description. |
| 18 |
| 19 Attributes: |
| 20 description: A JSON blob representing the job itself. |
| 21 created: A timestamp for when this job was posted. |
| 22 last_served: A timestamp for the last time this job was served, usually |
| 23 to a polling buildbot. Default is epoch 0, so new jobs are "old". |
| 24 taken: A boolean signalling that this job has been successfully picked |
| 25 up by a poller and can be dropped. |
| 26 """ |
| 27 description = ndb.JsonProperty() |
| 28 last_served = ndb.DateTimeProperty( |
| 29 default=datetime.datetime.utcfromtimestamp(0)) |
| 30 taken = ndb.BooleanProperty(default=False) |
| 31 |
| 32 |
| 33 class MainHandler(webapp2.RequestHandler): |
| 34 |
| 35 def get(self): |
| 36 self.response.write(""" |
| 37 <html> |
| 38 <body> |
| 39 <form action="/default/push" method="post"> |
| 40 <div><textarea name="job" rows="3" cols="60"></textarea></div> |
| 41 <div><input type="submit" value="Add Job"></div> |
| 42 </form> |
| 43 <form action="/default/accept" method="post"> |
| 44 <div><textarea name="job" rows="1" cols="60"></textarea></div> |
| 45 <div><input type="submit" value="Accept Job"></div> |
| 46 </form> |
| 47 </body> |
| 48 </html> |
| 49 """) |
| 50 |
| 51 |
| 52 class PushHandler(webapp2.RequestHandler): |
| 53 |
| 54 def post(self, project): |
| 55 job = Job(description=self.request.get('job'), namespace=project) |
| 56 job.put() |
| 57 |
| 58 |
| 59 class PullHandler(webapp2.RequestHandler): |
| 60 |
| 61 def post(self, project): |
| 62 # Get the jobs we'd like to serve. |
| 63 time_threshold = datetime.datetime.utcnow() - datetime.timedelta(seconds=30) |
| 64 query = Job.query(namespace=project) |
| 65 query = query.filter(Job.last_served < time_threshold) |
| 66 query = query.filter(Job.taken == False) |
| 67 query = query.order(Job.last_served) |
| 68 jobs = query.fetch(DEFAULT_JOBS_TO_SERVE) |
| 69 |
| 70 # Mark them as served. |
| 71 for job in jobs: |
| 72 job.last_served = datetime.datetime.utcnow() |
| 73 job.put() |
| 74 |
| 75 # Serve them. |
| 76 result = [] |
| 77 for job in jobs: |
| 78 job_blob = json.loads(job.description) |
| 79 job_blob.update({'job_key': job.key.urlsafe()}) |
| 80 result.append(job_blob) |
| 81 self.response.headers['Content-Type'] = 'application/json' |
| 82 self.response.write(json.dumps(result)) |
| 83 |
| 84 |
| 85 class PeekHandler(webapp2.RequestHandler): |
| 86 |
| 87 def get(self, project, job): |
| 88 # Get the jobs we'd like to serve. |
| 89 if job: |
| 90 job_key = ndb.Key(urlsafe=job, namespace=project) |
| 91 job = job_key.get() |
| 92 jobs = [job] |
| 93 else: |
| 94 time_threshold = (datetime.datetime.utcnow() - |
| 95 datetime.timedelta(seconds=30)) |
| 96 query = Job.query(namespace=project) |
| 97 query = query.filter(Job.last_served < time_threshold) |
| 98 query = query.filter(Job.taken == False) |
| 99 query = query.order(Job.last_served) |
| 100 jobs = query.fetch(DEFAULT_JOBS_TO_SERVE) |
| 101 |
| 102 # Serve them. |
| 103 result = [] |
| 104 for job in jobs: |
| 105 job_blob = json.loads(job.description) |
| 106 job_blob.update({'job_key': job.key.urlsafe()}) |
| 107 result.append(job_blob) |
| 108 self.response.headers['Content-Type'] = 'application/json' |
| 109 self.response.write(json.dumps(result)) |
| 110 |
| 111 |
| 112 class AcceptHandler(webapp2.RequestHandler): |
| 113 |
| 114 def post(self, project, job): |
| 115 job_key = ndb.Key(urlsafe=job, namespace=project) |
| 116 job = job_key.get() |
| 117 if job.taken: |
| 118 # This job has been previously accepted by someone else. |
| 119 self.response.set_status(409) |
| 120 job.taken = True |
| 121 job.put() |
| 122 |
| 123 |
| 124 app = webapp2.WSGIApplication([ |
| 125 ('/', MainHandler), |
| 126 ('/(.*)/push', PushHandler), |
| 127 ('/(.*)/pull', PullHandler), |
| 128 ('/(.*)/peek/?(.*)', PeekHandler), |
| 129 ('/(.*)/accept/(.*)', AcceptHandler), |
| 130 ]) |
OLD | NEW |