Chromium Code Reviews| Index: chromium-jobqueue/app.py |
| =================================================================== |
| --- chromium-jobqueue/app.py (revision 0) |
| +++ chromium-jobqueue/app.py (revision 0) |
| @@ -0,0 +1,129 @@ |
| +# 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
|
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import datetime |
| +import json |
| + |
| +import webapp2 |
| +from google.appengine.ext import ndb |
| + |
| + |
| +DEFAULT_NAMESPACE = 'default' |
| +DEFAULT_JOBS_TO_SERVE = 20 |
| + |
| + |
| +class Job(ndb.Model): |
| + """Represents a single tryjob description. |
| + |
| + Attributes: |
| + description: A JSON blob representing the job itself. |
| + created: A timestamp for when this job was posted. |
| + last_served: A timestamp for the last time this job was served to a polling |
| + 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.
|
| + taken: A boolean signalling that this job has been successfully picked up by |
| + a trysever and can be dropped. |
|
cmp
2013/08/22 04:44:12
tryserver -> other name
agable
2013/08/22 13:43:42
Done.
|
| + """ |
| + description = ndb.JsonProperty() |
| + last_served = ndb.DateTimeProperty( |
| + default=datetime.datetime.utcfromtimestamp(0)) |
| + taken = ndb.BooleanProperty(default=False) |
| + |
| + |
| +class MainHandler(webapp2.RequestHandler): |
| + |
| + def get(self): |
| + self.response.write(""" |
| +<html> |
| + <body> |
| + <form action="/default/push" method="post"> |
| + <div><textarea name="job" rows="3" cols="60"></textarea></div> |
| + <div><input type="submit" value="Add Job"></div> |
| + </form> |
| + <form action="/default/accept" method="post"> |
| + <div><textarea name="job" rows="1" cols="60"></textarea></div> |
| + <div><input type="submit" value="Accept Job"></div> |
| + </form> |
| + </body> |
| +</html> |
| +""") |
| + |
| + |
| +class PushHandler(webapp2.RequestHandler): |
| + |
| + def post(self, project): |
| + job = Job(description=self.request.get('job'), namespace=project) |
| + job.put() |
| + |
| + |
| +class PullHandler(webapp2.RequestHandler): |
| + |
| + def post(self, project): |
| + # Get the jobs we'd like to serve. |
| + time_threshold = datetime.datetime.utcnow() - datetime.timedelta(seconds=30) |
| + query = Job.query(namespace=project) |
| + query = query.filter(Job.last_served < time_threshold) |
| + query = query.filter(Job.taken == False) |
| + query = query.order(Job.last_served) |
| + jobs = query.fetch(DEFAULT_JOBS_TO_SERVE) |
| + |
| + # Mark them as served. |
| + for job in jobs: |
| + job.last_served = datetime.datetime.utcnow() |
| + job.put() |
| + |
| + # Serve them. |
| + result = [] |
| + for job in jobs: |
| + job_blob = json.loads(job.description) |
| + job_blob.update({'job_key': job.key.urlsafe()}) |
| + result.append(job_blob) |
| + self.response.headers['Content-Type'] = 'application/json' |
| + self.response.write(json.dumps(result)) |
| + |
| + |
| +class PeekHandler(webapp2.RequestHandler): |
| + |
| + def get(self, project, job): |
| + # Get the jobs we'd like to serve. |
| + if job: |
| + job_key = ndb.Key(urlsafe=job, namespace=project) |
| + job = job_key.get() |
| + jobs = [job] |
| + else: |
| + time_threshold = datetime.datetime.utcnow() - datetime.timedelta(seconds=30) |
|
cmp
2013/08/22 04:44:12
wrap at 80 chars
agable
2013/08/22 13:43:42
Done.
|
| + query = Job.query(namespace=project) |
| + query = query.filter(Job.last_served < time_threshold) |
| + query = query.filter(Job.taken == False) |
| + query = query.order(Job.last_served) |
| + jobs = query.fetch(DEFAULT_JOBS_TO_SERVE) |
| + |
| + # Serve them. |
| + result = [] |
| + for job in jobs: |
| + job_blob = json.loads(job.description) |
| + job_blob.update({'job_key': job.key.urlsafe()}) |
| + result.append(job_blob) |
| + self.response.headers['Content-Type'] = 'application/json' |
| + self.response.write(json.dumps(result)) |
| + |
| + |
| +class AcceptHandler(webapp2.RequestHandler): |
| + |
| + def post(self, project, job): |
| + job_key = ndb.Key(urlsafe=job, namespace=project) |
| + job = job_key.get() |
| + if job.taken: |
| + # This job has been previously accepted by someone else. |
| + self.response.set_status(409) |
| + 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
|
| + job.put() |
| + |
| + |
| +app = webapp2.WSGIApplication([ |
| + ('/', MainHandler), |
| + ('/(.*)/push', PushHandler), |
| + ('/(.*)/pull', PullHandler), |
| + ('/(.*)/peek/?(.*)', PeekHandler), |
| + ('/(.*)/accept/(.*)', AcceptHandler), |
| +]) |