This commit is contained in:
Kenneth Reitz
2012-04-04 02:47:23 -04:00
parent 69039c3b85
commit 94479b3451
5 changed files with 246 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
web: gunicorn springcreek:app -b 0.0.0.0:$PORT -k gevent -w 4
celeryd: ./manage.py celeryd --events --loglevel info
+25
View File
@@ -13,6 +13,31 @@ Plans
URI Schema::
/user
username
email
bio
website
location
date_joined
/users/
/users/:user/packs/
name
.. vanity_name?
description
patches
/users/:user/patches/
name
.. vanity_name?
description
distribution
device
/distributions/:id
hash
file_name
/devices/:slug
slug
vanity_name
make
model
approved
Executable
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flaskext.script import Manager
from flask.ext.celery import install_commands as install_celery
from springcreek import app
from springcreek.core import db, heroku
manager = Manager(app)
install_celery(manager)
@manager.command
def syncdb():
"""Initializes the database."""
db.create_all()
@manager.command
def destroy_all_software():
for i, app in enumerate(heroku.apps):
if i != 0:
app.destroy()
print 'destroyed {0}'.format(app.name)
if __name__ == "__main__":
manager.run()
+181
View File
@@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
"""
springcreek.core
~~~~~~~~~~~~~~~~
This module contains the main application of SpringCreek.
"""
import os
import tempfile
from datetime import datetime, timedelta
import heroku
import envoy
from flask import Flask, render_template, request, redirect, url_for, Response
from flask.views import MethodView
from flask_debugtoolbar import DebugToolbarExtension
from flask_heroku import Heroku
from flask_googlefed import GoogleAuth
from raven.contrib.flask import Sentry
from flask.ext.celery import Celery
from sqlalchemy import desc
from .models import db, BuildRequest, BuildResult
from .hacks import ContextTask, setup_ssh_keys, gen_lines
app = Flask(__name__)
# Don't ask.
setup_ssh_keys()
app.secret_key = 'some-secret-key'
# Use gevent workers for celery.
app.config['CELERYD_POOL'] = 'eventlet'
app.config['CELERYD_CONCURRENCY'] = 1000
# Heroku API Key.
app.config['HEROKU_API_KEY'] = os.environ.get('HEROKU_API_KEY')
app.config['DEBUG_TB_ENABLED'] = True
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
# Bootstrap Heroku environment variables.
heroku_env = Heroku(app)
# Intialize databse configuration.
db.init_app(app)
sentry = Sentry(app)
auth = GoogleAuth(app)
toolbar = DebugToolbarExtension(app)
celery = Celery(app)
heroku = heroku.from_key(app.config['HEROKU_API_KEY'] )
@celery.task(base=ContextTask)
def build_task(request_id, tail=False):
request = BuildRequest.query.filter_by(id=request_id).first()
result = request.result
app.logger.info('Starting build for {0}.'.format(request))
# Replace # w/ @
_split_url = request.application_url.split('#')
clone_url = _split_url[0]
clone_ref = _split_url[1] if len(_split_url) > 1 else 'HEAD'
h_app = heroku.apps.add(stack='cedar')
h_app.config['BUILDPACK_URL'] = request.buildpack_url
result.heroku_app = h_app.name
result.save()
app.logger.info('Heroku app {0} created for {1}.'.format(h_app, request))
pwd = tempfile.mkdtemp(prefix='springcreek')
app.logger.info('Temp dir {0} created for {1}.'.format(pwd, request))
os.chdir(pwd)
app.logger.info('Cloning {0} created for {1}.'.format(clone_url, request))
envoy.run('git clone {0} repo'.format(clone_url))
os.chdir('repo')
app.logger.info('Pushing app to {0} for {1}.'.format(h_app, request))
git_push = r'git push {result.git_url} {ref}:master'.format(result=result, ref=clone_ref)
app.logger.info(git_push)
if tail:
c = envoy.connect(git_push)
return c
c = envoy.run(git_push)
app.logger.info('Push exit code: {c.status_code}'.format(c=c))
result.success = (c.status_code == 0)
result.install_log = c.std_err
result.active = True
result.save()
if not request.keep:
later = datetime.now() + timedelta(hours=2)
destroy_app.apply_async(args=[request_id], eta=later)
@celery.task(base=ContextTask)
def destroy_app(request_id):
request = BuildRequest.query.filter_by(id=request_id).first()
result = request.result
app.logger.info('Destroying app {0}.'.format(result.heroku_app))
# Destroy the app.
h_app = heroku.apps[result.heroku_app]
h_app.destroy()
result.active = False
result.save()
@app.route('/')
@auth.required
def landing_page():
return render_template('index.html')
class Builds(MethodView):
@auth.required
def get(self):
builds = BuildRequest.query.order_by(desc(BuildRequest.created)).all()
return render_template('builds.html', builds=builds)
@auth.required
def post(self):
"""Create a new BuildRequest."""
r = BuildRequest(
buildpack_url=request.form.get('buildpack_url'),
application_url=request.form.get('application_url'),
keep=('keep' in request.form)
)
db.session.add(r)
db.session.commit()
if request.args.get('tail'):
c = build_task(r.id, tail=True)
return Response(gen_lines(c))
else:
# Send the build task off to work.
build_task.delay(r.id, tail=False)
return redirect(r.url)
app.add_url_rule('/builds', view_func=Builds.as_view('builds'))
@app.route('/builds/<id>')
@auth.required
def view_build(id):
b_request = BuildRequest.query.filter_by(id=id).first()
context = dict(
request=b_request
)
if b_request:
return render_template('build.html', **context)
else:
return redirect(url_for('builds'))
if __name__ == '__main__':
app.run()
+7
View File
@@ -2,6 +2,7 @@ Flask==0.8
Flask-Principal==0.2
Flask-Script==0.3.3
Jinja2==2.6
Logbook==0.3
Werkzeug==0.8.3
argparse==1.2.1
blinker==1.2
@@ -11,7 +12,13 @@ flask-heroku==0.1.2
gevent==0.13.6
greenlet==0.3.4
gunicorn==0.14.2
procname==0.3
python-dateutil==1.5
pytz==2012b
raven==1.5.0
redis==2.4.11
rq==0.1.0
setproctitle==1.1.5
simplejson==2.5.0
times==0.4
wsgiref==0.1.2