From f572749acec46180307ddad14d09e304e8a5515a Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 12:43:25 -0400
Subject: [PATCH 01/10] Fix a bug in billing dealing with former customers.

---
 endpoints/api.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/endpoints/api.py b/endpoints/api.py
index a550a58cf..b44aa03eb 100644
--- a/endpoints/api.py
+++ b/endpoints/api.py
@@ -401,7 +401,9 @@ def get_subscription():
   if user.stripe_id:
     private_repos = model.get_private_repo_count(user.username)
     cus = stripe.Customer.retrieve(user.stripe_id)
-    return jsonify(subscription_view(cus.subscription, private_repos))
+
+    if cus.subscription:
+      return jsonify(subscription_view(cus.subscription, private_repos))
 
   abort(404)
 

From 39018e72f1a14f6c46763719cbe513943994d53f Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 12:43:45 -0400
Subject: [PATCH 02/10] Open and close the db connection at the beginning and
 end of each request.

---
 data/database.py | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/data/database.py b/data/database.py
index dba84fa8d..c5b5e7e88 100644
--- a/data/database.py
+++ b/data/database.py
@@ -1,4 +1,5 @@
 import string
+import logging
 
 from random import SystemRandom
 from datetime import datetime
@@ -8,10 +9,26 @@ from peewee import create_model_tables
 from app import app
 
 
+logger = logging.getLogger(__name__)
 db = app.config['DB_DRIVER'](app.config['DB_NAME'],
                              **app.config['DB_CONNECTION_ARGS'])
 
 
+def connect_db():
+  logger.debug('Connectin to database.')
+  db.connect()
+
+
+def close_db(exc):
+  if not db.is_closed():
+    logger.debug('Disconnecting from database.')
+    db.close()
+
+
+app.before_request(connect_db)
+app.teardown_request(close_db)
+
+
 class BaseModel(Model):
   class Meta:
     database = db

From 1f81163d19450e9e056e9f874c9c0170e3cca254 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 13:03:09 -0400
Subject: [PATCH 03/10] Allow chunked transfer encoding.

---
 wsgi.conf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/wsgi.conf b/wsgi.conf
index d269b6912..d4c459e2b 100644
--- a/wsgi.conf
+++ b/wsgi.conf
@@ -4,6 +4,7 @@ WSGIPythonHome /opt/python/run/venv
 WSGISocketPrefix run/wsgi
 WSGIRestrictEmbedded On
 WSGIPassAuthorization On
+WSGIChunkedRequest On
 
 <VirtualHost *:80>
   SetEnvIf X-Forwarded-Proto https HTTPS=1

From 70a9e791578a30968ad5b4fd2c7dcaac1e23023a Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 14:06:12 -0400
Subject: [PATCH 04/10] Add extra logging to figure out why checksums don't
 match in prod.

---
 endpoints/registry.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/endpoints/registry.py b/endpoints/registry.py
index dc9b31590..7be84d6df 100644
--- a/endpoints/registry.py
+++ b/endpoints/registry.py
@@ -168,6 +168,8 @@ def put_image_checksum(namespace, repository, image_id):
   if err:
     abort(err)
   if checksum not in session.get('checksum', []):
+    logger.debug('session checksums: %s' % session.get('checksum', []))
+    logger.debug('client supplied checksum: %s' % checksum)
     logger.debug('put_image_layer: Wrong checksum')
     abort(400)  #'Checksum mismatch')
   # Checksum is ok, we remove the marker

From 504bc7e04ed155985c4f255b3d8f9ee0d155ccbc Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 14:35:21 -0400
Subject: [PATCH 05/10] Add a status endpoint that we can use to test if the
 instance is serving traffic.

---
 endpoints/web.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/endpoints/web.py b/endpoints/web.py
index 37caecefb..f1c8ccfa2 100644
--- a/endpoints/web.py
+++ b/endpoints/web.py
@@ -1,7 +1,7 @@
 import logging
 
 from flask import (abort, send_file, redirect, request, url_for,
-                   render_template)
+                   render_template, make_response)
 from flask.ext.login import login_user, UserMixin, login_required, logout_user
 from flask.ext.principal import identity_changed, Identity, AnonymousIdentity
 
@@ -38,6 +38,11 @@ def index():
   return send_file('templates/index.html')
 
 
+@app.route('/status', methods=['GET'])
+def status():
+  return make_response('Healthy')
+
+
 @app.route('/tos', methods=['GET'])
 def tos():
   return send_file('templates/tos.html')

From 2ff96ffd8760138b26a251cf9396bd5bc5e289ed Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 14:35:54 -0400
Subject: [PATCH 06/10] Try to use the presense of http to mark a connection as
 insecure rather than using the presense of https to make it as secure.

---
 wsgi.conf | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/wsgi.conf b/wsgi.conf
index d4c459e2b..f5df7de10 100644
--- a/wsgi.conf
+++ b/wsgi.conf
@@ -7,11 +7,11 @@ WSGIPassAuthorization On
 WSGIChunkedRequest On
 
 <VirtualHost *:80>
-  SetEnvIf X-Forwarded-Proto https HTTPS=1
+  SetEnvIf X-Forwarded-Proto !http HTTPS=1
 
   RewriteEngine On
-  RewriteCond %{HTTP:X-Forwarded-Proto} !https
-  RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R=permanent]
+  RewriteCond %{HTTP:X-Forwarded-Proto} http
+  RewriteRule !/status https://%{SERVER_NAME}%{REQUEST_URI} [L,R=permanent]
 
   Alias /static /opt/python/current/app/static/
   <Directory /opt/python/current/app/>

From 16623bb520c327aa8e022a82de7b18f2ba9e017b Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 14:40:28 -0400
Subject: [PATCH 07/10] Temporarily disable the http->https redirect.

---
 wsgi.conf | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/wsgi.conf b/wsgi.conf
index f5df7de10..93e6338b1 100644
--- a/wsgi.conf
+++ b/wsgi.conf
@@ -7,12 +7,6 @@ WSGIPassAuthorization On
 WSGIChunkedRequest On
 
 <VirtualHost *:80>
-  SetEnvIf X-Forwarded-Proto !http HTTPS=1
-
-  RewriteEngine On
-  RewriteCond %{HTTP:X-Forwarded-Proto} http
-  RewriteRule !/status https://%{SERVER_NAME}%{REQUEST_URI} [L,R=permanent]
-
   Alias /static /opt/python/current/app/static/
   <Directory /opt/python/current/app/>
     Order allow,deny

From fc80616eab77b0f4100bf6043d725fbe099b9219 Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-10-34-98-98.ec2.internal>
Date: Wed, 2 Oct 2013 20:42:12 +0000
Subject: [PATCH 08/10] Set up for running under gunicorn and nginx.

---
 README.md              |   4 ++
 certs/unified.cert     | 121 +++++++++++++++++++++++++++++++++++++++++
 config.py              |   1 +
 nginx.conf             |  61 +++++++++++++++++++++
 requirements-nover.txt |   4 +-
 requirements.txt       |  39 +++++++++++--
 6 files changed, 224 insertions(+), 6 deletions(-)
 create mode 100644 README.md
 create mode 100644 certs/unified.cert
 create mode 100644 nginx.conf

diff --git a/README.md b/README.md
new file mode 100644
index 000000000..c3a52a523
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+running:
+
+sudo nginx -c `pwd`/nginx.conf
+STACK=prod gunicorn -D --workers 4 -b unix:/tmp/gunicorn.sock --worker-class eventlet -t 500 application:application
diff --git a/certs/unified.cert b/certs/unified.cert
new file mode 100644
index 000000000..2d94e9306
--- /dev/null
+++ b/certs/unified.cert
@@ -0,0 +1,121 @@
+-----BEGIN CERTIFICATE-----
+MIIHRjCCBi6gAwIBAgIDDDb4MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
+TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
+YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg
+MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTMwOTMwMTUxMTI3
+WhcNMTQxMDAxMDYyMDAxWjBhMRkwFwYDVQQNExBlNEZTNTBhYmNYcmQyZnlJMQsw
+CQYDVQQGEwJVUzEUMBIGA1UEAxMLd3d3LnF1YXkuaW8xITAfBgkqhkiG9w0BCQEW
+Emhvc3RtYXN0ZXJAcXVheS5pbzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBANGOItO9zOeJES+cQjB/8scbkLghi8wIvFnw/VJUUYsFrRYF2PJ96nrd0hcM
+te/cvlU9phw6zhlay1zb8OuIAhtgIYFcKw/t41F7DRZGj+JaT620D5jFebWgLbLf
+pxWnqGfGR4x5XgZOvzpWUgFBnX+KzvzwqfZndRLBBjpq2Rau30zggS6ff2iUNwPZ
+8vPHUv/RQ6XVzq0WtbJQ1B3KVwSwcd9Eclg15LrWBd6RQxIl84CYDO6vhl00D6C8
+x8lvTjW+nB8mnnGS4F8pa3i5euwCMXWepO8EFGpeK4QikOFTevYAx1BUHeE/MGJX
+FfPVIjhFVzWSrCnE2YjUcUAYoOnv0ZltpBFgsPUKyWZ4ZN3vbToorm4OYu9SJYtJ
+FP51OsTizuyC85hm9zA03D3pf7zOIwIWwTG2ZdmKW4g3gNt8EJv25QC9vSiPmLa4
+wWzHgeRiMc7W9+lEive7HDafVBZQ3DX05qRbsYijhXTW6iojw0YntP5o3ndK/9Id
+WfuP0cQxwxtAy7ykmnPUZ0ES58Hmf63QQ+unWhqO2nfbw/741/zC+ryyf0hcJmac
+lS0Yjnisk4R62MOiRzyYxw0h8UBHBJvAzsNi+ouLtkEm8F8ne6wawGcXixwHPQnc
+52XCcYZsguVwa5Pohh6/rcisTTJ3P9NSouFw4l2ghcrbwPALAgMBAAGjggLZMIIC
+1TAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAd
+BgNVHQ4EFgQUkty8z9tltZ1SV8qVzUHTjRewBwswHwYDVR0jBBgwFoAU60I00Jiw
+q5/0G2sI98xkLu8OLEUwHwYDVR0RBBgwFoILd3d3LnF1YXkuaW+CB3F1YXkuaW8w
+ggFWBgNVHSAEggFNMIIBSTAIBgZngQwBAgEwggE7BgsrBgEEAYG1NwECAzCCASow
+LgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYw
+gfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3JkaW5n
+IHRvIHRoZSBDbGFzcyAxIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRoZSBT
+dGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRlbmRl
+ZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkgb2Js
+aWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuc3RhcnRzc2wu
+Y29tL2NydDEtY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEwfzA5BggrBgEFBQcwAYYt
+aHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczEvc2VydmVyL2NhMEIG
+CCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIuY2xh
+c3MxLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5zdGFydHNz
+bC5jb20vMA0GCSqGSIb3DQEBBQUAA4IBAQAFwzBHJ7d/Lutu/ub6gSDdMDvwAmWp
+gsspoEqJJwFLPiVC3s7Ejj2qM20t2FZ4hEHPoQlfqQa/AmxQCLQnY5EnfJLZawwn
+dYCFAbkGWvxa11ROHHv5gBovltBGef9lHuNzIc9hkkSpUBzH+nakGAdMs5MD+NH1
+f6GnsIOXLWlB14WHFSyN/v/bTZGkoq/9Tgkl4v4AkoZz6Fpd1XmZ4EXrZVBW/wWc
+vnhKe3Khx2xU5xlU0GdtAd7WhasZhzyy7OxUvLqlnJygBYTg2R7RYF8jF3r85PU+
+NPo9t8Xl8I+y7JiD10WRJQdd34yK6DJlmr358J0nWWk7PQv6p5Cpx475
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
+jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
+IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
+YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
+gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
+pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
+kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
+ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
+xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
+AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
+L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
+YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
+c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
+BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
+BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
+LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
+tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
+xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
+xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
+t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
+RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
+YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
+WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
+SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
+wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
+p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
+0q6Dp6jOW6c=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
+ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
+LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
+BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
+dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
+cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
+YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
+dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
+bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
+YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
+TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
+9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
+jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
+FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
+ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
+ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
+EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
+L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
+O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
+um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
+NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
+-----END CERTIFICATE-----
diff --git a/config.py b/config.py
index 6b50fe912..dbbcef827 100644
--- a/config.py
+++ b/config.py
@@ -89,4 +89,5 @@ class ProductionConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL,
     'stream': sys.stderr,
     'level': logging.DEBUG,
     'format': LOG_FORMAT,
+    'filename': 'application.log',
   }
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 000000000..4efb0a7f1
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,61 @@
+worker_processes 1;
+
+user nobody nogroup;
+pid /tmp/nginx.pid;
+error_log /tmp/nginx.error.log;
+
+events {
+    worker_connections 1024;
+    accept_mutex off;
+}
+
+http {
+    default_type application/octet-stream;
+    access_log /tmp/nginx.access.log combined;
+    sendfile on;
+
+    upstream app_server {
+        server unix:/tmp/gunicorn.sock fail_timeout=0;
+        # For a TCP configuration:
+        # server 192.168.0.7:8000 fail_timeout=0;
+    }
+
+    server {
+        listen 80 default_server;
+        server_name _;
+        rewrite ^ https://$host$request_uri? permanent;
+    }
+
+    server {
+        listen 443 default;
+        client_max_body_size 4G;
+        server_name _;
+
+        keepalive_timeout 5;
+
+        # path for static files
+        root /home/ubuntu/quay/static;
+
+        ssl on;
+        ssl_certificate /home/ubuntu/quay/certs/unified.cert;
+        ssl_certificate_key /home/ubuntu/quay/certs/quay.key;
+        ssl_session_timeout 5m;
+        ssl_protocols SSLv3 TLSv1;
+        ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
+        ssl_prefer_server_ciphers on;
+
+        location / {
+            # checks for static file, if not found proxy to app
+            try_files $uri @proxy_to_app;
+        }
+
+        location @proxy_to_app {
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_set_header Host $http_host;
+            proxy_redirect off;
+            proxy_buffering off;
+
+            proxy_pass   http://app_server;
+        }
+    }
+}
diff --git a/requirements-nover.txt b/requirements-nover.txt
index c2c205e79..7eee78b9c 100644
--- a/requirements-nover.txt
+++ b/requirements-nover.txt
@@ -7,4 +7,6 @@ Flask-Mail
 python-dateutil
 boto
 pymysql
-stripe
\ No newline at end of file
+stripe
+gunicorn
+eventlet
diff --git a/requirements.txt b/requirements.txt
index 45761457c..38d9983f4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,22 +1,51 @@
+Cheetah==2.4.4
 Flask==0.10.1
 Flask-Login==0.2.7
 Flask-Mail==0.9.0
 Flask-Principal==0.4.0
 Jinja2==2.7.1
+Landscape-Client==12.12
+M2Crypto==0.21.1
 MarkupSafe==0.18
+PAM==0.4.2
 PyMySQL==0.5
+PyYAML==3.10
+Twisted-Core==12.3.0
+Twisted-Names==12.3.0
+Twisted-Web==12.3.0
 Werkzeug==0.9.4
+apt-xapian-index==0.45
 argparse==1.2.1
 blinker==1.3
-boto==2.13.3
+boto==2.3.0
+chardet==2.0.1
+cloud-init==0.7.2
+configobj==4.7.2
 distribute==0.6.34
-ipdb==0.8
-ipython==1.1.0
+distro-info==0.10
+euca2ools==2.1.1
+eventlet==0.14.0
+greenlet==0.4.1
+gunicorn==18.0
 itsdangerous==0.23
+oauth==1.0.1
+paramiko==1.7.7.1
 peewee==2.1.4
+prettytable==0.6.1
 py-bcrypt==0.4
+pyOpenSSL==0.13
+pycrypto==2.6
+pycurl==7.19.0
+pygobject==3.8.0
+pyserial==2.6
+python-apt==0.8.8ubuntu6
 python-dateutil==2.1
-requests==2.0.0
-six==1.4.1
+python-debian==0.1.21-nmu2ubuntu1
+requests==1.1.0
+six==1.2.0
+ssh-import-id==3.14
 stripe==1.9.5
+urllib3==1.5
+virtualenv==1.9.1
 wsgiref==0.1.2
+zope.interface==4.0.5

From 06b1e1466f90b671847bef861e96e10946a74ab7 Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-10-34-98-98.ec2.internal>
Date: Wed, 2 Oct 2013 21:21:22 +0000
Subject: [PATCH 09/10] Have nginx serve the static files to not overwhelm
 flask.

---
 nginx.conf | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/nginx.conf b/nginx.conf
index 4efb0a7f1..c7209270e 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -10,6 +10,8 @@ events {
 }
 
 http {
+    include /etc/nginx/mime.types;
+
     default_type application/octet-stream;
     access_log /tmp/nginx.access.log combined;
     sendfile on;
@@ -33,9 +35,6 @@ http {
 
         keepalive_timeout 5;
 
-        # path for static files
-        root /home/ubuntu/quay/static;
-
         ssl on;
         ssl_certificate /home/ubuntu/quay/certs/unified.cert;
         ssl_certificate_key /home/ubuntu/quay/certs/quay.key;
@@ -44,13 +43,14 @@ http {
         ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
         ssl_prefer_server_ciphers on;
 
-        location / {
+        location /static/ {
             # checks for static file, if not found proxy to app
-            try_files $uri @proxy_to_app;
+            alias /home/ubuntu/quay/static/;
         }
 
-        location @proxy_to_app {
+        location / {
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            proxy_set_header X-Forwarded-Proto $scheme;
             proxy_set_header Host $http_host;
             proxy_redirect off;
             proxy_buffering off;

From 9cbbc2b8ed4ce6a8c0a91f3fa89b4d92d451a781 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Wed, 2 Oct 2013 17:29:45 -0400
Subject: [PATCH 10/10] Fix image history with no parent.

---
 data/model.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/data/model.py b/data/model.py
index 04f34e6fc..97db86cc5 100644
--- a/data/model.py
+++ b/data/model.py
@@ -287,6 +287,9 @@ def get_parent_images(image_obj):
   parents = image_obj.ancestors
   parent_db_ids = parents.strip('/').split('/')
 
+  if parent_db_ids == ['']:
+    return []
+
   or_clauses = [(Image.id == db_id) for db_id in parent_db_ids]
   parent_images = Image.select().where(reduce(operator.or_, or_clauses))
   id_to_image = {unicode(image.id): image for image in parent_images}