blob: 3a0c042a9e51896a67117497b27551591a84ee88 [file] [log] [blame]
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# 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 build job 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, usually
to a polling buildbot. Default is epoch 0, so new jobs are "old".
taken: A boolean signalling that this job has been successfully picked
up by a poller and can be dropped.
"""
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))
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
job.put()
app = webapp2.WSGIApplication([
('/', MainHandler),
('/(.*)/push', PushHandler),
('/(.*)/pull', PullHandler),
('/(.*)/peek/?(.*)', PeekHandler),
('/(.*)/accept/(.*)', AcceptHandler),
])