Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
|
cmp
2013/08/22 04:44:12
2013?
There should be a LICENSE file in the CL, t
agable
2013/08/22 13:43:42
Done, copied from depot_tools. Out of curiosity, w
| |
| 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 tryjob 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 to a polling | |
| 23 tryserver. Default is epoch 0, so new jobs are "old". | |
|
cmp
2013/08/22 04:44:12
tryserver -> other name
agable
2013/08/22 13:43:42
Done.
| |
| 24 taken: A boolean signalling that this job has been successfully picked up by | |
| 25 a trysever and can be dropped. | |
|
cmp
2013/08/22 04:44:12
tryserver -> other name
agable
2013/08/22 13:43:42
Done.
| |
| 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() - datetime.timedelta(seconds=3 0) | |
|
cmp
2013/08/22 04:44:12
wrap at 80 chars
agable
2013/08/22 13:43:42
Done.
| |
| 95 query = Job.query(namespace=project) | |
| 96 query = query.filter(Job.last_served < time_threshold) | |
| 97 query = query.filter(Job.taken == False) | |
| 98 query = query.order(Job.last_served) | |
| 99 jobs = query.fetch(DEFAULT_JOBS_TO_SERVE) | |
| 100 | |
| 101 # Serve them. | |
| 102 result = [] | |
| 103 for job in jobs: | |
| 104 job_blob = json.loads(job.description) | |
| 105 job_blob.update({'job_key': job.key.urlsafe()}) | |
| 106 result.append(job_blob) | |
| 107 self.response.headers['Content-Type'] = 'application/json' | |
| 108 self.response.write(json.dumps(result)) | |
| 109 | |
| 110 | |
| 111 class AcceptHandler(webapp2.RequestHandler): | |
| 112 | |
| 113 def post(self, project, job): | |
| 114 job_key = ndb.Key(urlsafe=job, namespace=project) | |
| 115 job = job_key.get() | |
| 116 if job.taken: | |
| 117 # This job has been previously accepted by someone else. | |
| 118 self.response.set_status(409) | |
| 119 job.taken = True | |
|
cmp
2013/08/22 04:44:12
I think this would be a perfect place to use pull
agable
2013/08/22 13:43:42
Agreed. Right now, the Pull/Accept paradigm is jus
| |
| 120 job.put() | |
| 121 | |
| 122 | |
| 123 app = webapp2.WSGIApplication([ | |
| 124 ('/', MainHandler), | |
| 125 ('/(.*)/push', PushHandler), | |
| 126 ('/(.*)/pull', PullHandler), | |
| 127 ('/(.*)/peek/?(.*)', PeekHandler), | |
| 128 ('/(.*)/accept/(.*)', AcceptHandler), | |
| 129 ]) | |
| OLD | NEW |