From 84adf680b99bf8d919cf79d15b05c0149a4f6ce6 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 19:10:27 -0400
Subject: [PATCH 01/13] Switch the registry and index to use real s3 and rds.

---
 config.py              |  39 +++++++-
 data/database.py       |  15 ++--
 requirements-nover.txt |   3 +-
 requirements.txt       |   1 +
 storage/__init__.py    | 200 +++++++++++++++++++++--------------------
 5 files changed, 154 insertions(+), 104 deletions(-)

diff --git a/config.py b/config.py
index 136d11945..b6242b116 100644
--- a/config.py
+++ b/config.py
@@ -1,3 +1,5 @@
+from peewee import MySQLDatabase, SqliteDatabase
+
 class FlaskConfig(object):
   SECRET_KEY = '1cb18882-6d12-440d-a4cc-b7430fb5f884'
 
@@ -13,5 +15,40 @@ class MailConfig(object):
   TESTING = False
 
 
-class ProductionConfig(FlaskConfig, MailConfig):
+class SQLiteDB(object):
+  DB_NAME = 'test.db'
+  DB_CONNECTION_ARGS = {
+    'threadlocals': True
+  }
+  DB_DRIVER = SqliteDatabase
+
+
+class RDSMySQL(object):
+  DB_NAME = 'quay'
+  DB_CONNECTION_ARGS = {
+    'host': 'fluxmonkeylogin.cb0vumcygprn.us-east-1.rds.amazonaws.com',
+    'user': 'fluxmonkey',
+    'passwd': '8eifM#uoZ85xqC^',
+    'threadlocals': True
+  }
+  DB_DRIVER = MySQLDatabase
+
+
+class S3Storage(object):
+  AWS_ACCESS_KEY = 'AKIAJWZWUIS24TWSMWRA'
+  AWS_SECRET_KEY = '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'
+  REGISTRY_S3_BUCKET = 'quay-registry'
+  STORAGE_KIND = 's3'
+
+
+class LocalStorage(object):
+  STORAGE_KIND = 'local'
+  LOCAL_STORAGE_DIR = '/tmp/registry'
+
+
+class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB):
   REGISTRY_SERVER = 'localhost:5000'
+
+
+class ProductionConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL):
+  REGISTRY_SERVER = 'quay.io'
diff --git a/data/database.py b/data/database.py
index 13791af53..3a377d02c 100644
--- a/data/database.py
+++ b/data/database.py
@@ -1,12 +1,15 @@
 import string
-from random import SystemRandom
 
+from random import SystemRandom
+from datetime import datetime
 from peewee import *
 from peewee import create_model_tables
 
-from datetime import datetime
+from app import app
 
-db = SqliteDatabase('test.db', threadlocals=True)
+
+db = app.config['DB_DRIVER'](app.config['DB_NAME'],
+                             **app.config['DB_CONNECTION_ARGS'])
 
 
 class BaseModel(Model):
@@ -29,7 +32,7 @@ class Repository(BaseModel):
   namespace = CharField()
   name = CharField()
   visibility = ForeignKeyField(Visibility)
-  description = CharField(null=True)
+  description = BlobField(null=True)
 
   class Meta:
     database = db
@@ -86,11 +89,11 @@ class Image(BaseModel):
   image_id = CharField()
   checksum = CharField(null=True)
   created = DateTimeField(null=True)
-  comment = CharField(null=True)
+  comment = BlobField(null=True)
   repository = ForeignKeyField(Repository)
 
   # '/' separated list of ancestory ids, e.g. /1/2/6/7/10/
-  ancestors = CharField(index=True, default='/', max_length=65535)
+  ancestors = CharField(index=True, default='/', max_length=64535)
 
   class Meta:
     database = db
diff --git a/requirements-nover.txt b/requirements-nover.txt
index 7cdb7d4b6..4f7a3d3b2 100644
--- a/requirements-nover.txt
+++ b/requirements-nover.txt
@@ -5,4 +5,5 @@ Flask-Principal
 Flask-Login
 Flask-Mail
 python-dateutil
-boto
\ No newline at end of file
+boto
+MySQL-python
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 2c28e828a..6543f5c24 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@ Flask-Mail==0.9.0
 Flask-Principal==0.4.0
 Jinja2==2.7.1
 MarkupSafe==0.18
+MySQL-python==1.2.4
 Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
diff --git a/storage/__init__.py b/storage/__init__.py
index 2a67815f9..ddcc5948c 100644
--- a/storage/__init__.py
+++ b/storage/__init__.py
@@ -1,119 +1,123 @@
+import logging
 
 import contextlib
 import tempfile
 
+from app import app
+
 
 __all__ = ['load']
+logger = logging.getLogger(__name__)
 
 
 class Storage(object):
 
-    """Storage is organized as follow:
-    $ROOT/images/<image_id>/json
-    $ROOT/images/<image_id>/layer
-    $ROOT/repositories/<namespace>/<repository_name>/<tag_name>
-    """
+  """Storage is organized as follow:
+  $ROOT/images/<image_id>/json
+  $ROOT/images/<image_id>/layer
+  $ROOT/repositories/<namespace>/<repository_name>/<tag_name>
+  """
 
-    # Useful if we want to change those locations later without rewriting
-    # the code which uses Storage
-    repositories = 'repositories'
-    images = 'images'
-    # Set the IO buffer to 64kB
-    buffer_size = 64 * 1024
+  # Useful if we want to change those locations later without rewriting
+  # the code which uses Storage
+  repositories = 'repositories'
+  images = 'images'
+  # Set the IO buffer to 64kB
+  buffer_size = 64 * 1024
 
-    #FIXME(samalba): Move all path resolver in each module (out of the base)
-    def images_list_path(self, namespace, repository):
-        return '{0}/{1}/{2}/_images_list'.format(self.repositories,
-                                                 namespace,
-                                                 repository)
+  #FIXME(samalba): Move all path resolver in each module (out of the base)
+  def images_list_path(self, namespace, repository):
+    return '{0}/{1}/{2}/_images_list'.format(self.repositories,
+                         namespace,
+                         repository)
 
-    def image_json_path(self, namespace, repository, image_id):
-        return '{0}/{1}/{2}/{3}/json'.format(self.images, namespace,
-                                             repository, image_id)
+  def image_json_path(self, namespace, repository, image_id):
+    return '{0}/{1}/{2}/{3}/json'.format(self.images, namespace,
+                       repository, image_id)
 
-    def image_mark_path(self, namespace, repository, image_id):
-        return '{0}/{1}/{2}/{3}/_inprogress'.format(self.images, namespace,
-                                                    repository, image_id)
+  def image_mark_path(self, namespace, repository, image_id):
+    return '{0}/{1}/{2}/{3}/_inprogress'.format(self.images, namespace,
+                          repository, image_id)
 
-    def image_checksum_path(self, namespace, repository, image_id):
-        return '{0}/{1}/{2}/{3}/_checksum'.format(self.images, namespace,
-                                                  repository, image_id)
+  def image_checksum_path(self, namespace, repository, image_id):
+    return '{0}/{1}/{2}/{3}/_checksum'.format(self.images, namespace,
+                          repository, image_id)
 
-    def image_layer_path(self, namespace, repository, image_id):
-        return '{0}/{1}/{2}/{3}/layer'.format(self.images, namespace,
-                                              repository, image_id)
+  def image_layer_path(self, namespace, repository, image_id):
+    return '{0}/{1}/{2}/{3}/layer'.format(self.images, namespace,
+                        repository, image_id)
 
-    def image_ancestry_path(self, namespace, repository, image_id):
-        return '{0}/{1}/{2}/{3}/ancestry'.format(self.images, namespace,
-                                                 repository, image_id)
+  def image_ancestry_path(self, namespace, repository, image_id):
+    return '{0}/{1}/{2}/{3}/ancestry'.format(self.images, namespace,
+                         repository, image_id)
 
-    def tag_path(self, namespace, repository, tagname=None):
-        if not tagname:
-            return '{0}/{1}/{2}'.format(self.repositories,
-                                        namespace,
-                                        repository)
-        return '{0}/{1}/{2}/tag_{3}'.format(self.repositories,
-                                            namespace,
-                                            repository,
-                                            tagname)
+  def tag_path(self, namespace, repository, tagname=None):
+    if not tagname:
+      return '{0}/{1}/{2}'.format(self.repositories,
+                    namespace,
+                    repository)
+    return '{0}/{1}/{2}/tag_{3}'.format(self.repositories,
+                      namespace,
+                      repository,
+                      tagname)
 
-    def index_images_path(self, namespace, repository):
-        return '{0}/{1}/{2}/_index_images'.format(self.repositories,
-                                                  namespace,
-                                                  repository)
+  def index_images_path(self, namespace, repository):
+    return '{0}/{1}/{2}/_index_images'.format(self.repositories,
+                          namespace,
+                          repository)
 
-    def get_content(self, path):
-        raise NotImplementedError
+  def get_content(self, path):
+    raise NotImplementedError
 
-    def put_content(self, path, content):
-        raise NotImplementedError
+  def put_content(self, path, content):
+    raise NotImplementedError
 
-    def stream_read(self, path):
-        raise NotImplementedError
+  def stream_read(self, path):
+    raise NotImplementedError
 
-    def stream_write(self, path, fp):
-        raise NotImplementedError
+  def stream_write(self, path, fp):
+    raise NotImplementedError
 
-    def list_directory(self, path=None):
-        raise NotImplementedError
+  def list_directory(self, path=None):
+    raise NotImplementedError
 
-    def exists(self, path):
-        raise NotImplementedError
+  def exists(self, path):
+    raise NotImplementedError
 
-    def remove(self, path):
-        raise NotImplementedError
+  def remove(self, path):
+    raise NotImplementedError
 
-    def get_size(self, path):
-        raise NotImplementedError
+  def get_size(self, path):
+    raise NotImplementedError
 
 
 @contextlib.contextmanager
 def store_stream(stream):
-    """Stores the entire stream to a temporary file."""
-    tmpf = tempfile.TemporaryFile()
-    while True:
-        try:
-            buf = stream.read(4096)
-            if not buf:
-                break
-            tmpf.write(buf)
-        except IOError:
-            break
-    tmpf.seek(0)
-    yield tmpf
-    tmpf.close()
+  """Stores the entire stream to a temporary file."""
+  tmpf = tempfile.TemporaryFile()
+  while True:
+    try:
+      buf = stream.read(4096)
+      if not buf:
+        break
+      tmpf.write(buf)
+    except IOError:
+      break
+  tmpf.seek(0)
+  yield tmpf
+  tmpf.close()
 
 
 def temp_store_handler():
-    tmpf = tempfile.TemporaryFile()
+  tmpf = tempfile.TemporaryFile()
 
-    def fn(buf):
-        try:
-            tmpf.write(buf)
-        except IOError:
-            pass
+  def fn(buf):
+    try:
+      tmpf.write(buf)
+    except IOError:
+      pass
 
-    return tmpf, fn
+  return tmpf, fn
 
 
 from local import LocalStorage
@@ -124,20 +128,24 @@ _storage = {}
 
 
 def load(kind=None):
-    """Returns the right storage class according to the configuration."""
-    global _storage
+  """Returns the right storage class according to the configuration."""
+  global _storage
 
-    # TODO hard code to local for now
-    kind = 'local'
-    # if not kind:
-    #     kind = cfg.storage.lower()
-    if kind in _storage:
-        return _storage[kind]
-    if kind == 's3':
-        store = S3Storage('/registry', 'access_key', 'secret_key', 'bucket')
-    elif kind == 'local':
-        store = LocalStorage('/tmp/registry')
-    else:
-        raise ValueError('Not supported storage \'{0}\''.format(kind))
-    _storage[kind] = store
-    return store
+  # TODO hard code to local for now
+  kind = app.config['STORAGE_KIND']
+  # if not kind:
+  #     kind = cfg.storage.lower()
+  if kind in _storage:
+    return _storage[kind]
+  if kind == 's3':
+    logger.debug('Using s3 storage.')
+    store = S3Storage('', app.config['AWS_ACCESS_KEY'],
+                      app.config['AWS_SECRET_KEY'],
+                      app.config['REGISTRY_S3_BUCKET'])
+  elif kind == 'local':
+    logger.debug('Using local storage.')
+    store = LocalStorage(app.config['LOCAL_STORAGE_DIR'])
+  else:
+    raise ValueError('Not supported storage \'{0}\''.format(kind))
+  _storage[kind] = store
+  return store

From 22edde9cb4c4820c94dfffe3eaffd56580128da7 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 19:28:51 -0400
Subject: [PATCH 02/13] It was the distribute library not mysql causing
 problems.

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 6543f5c24..6a05826f5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@ Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
-distribute==0.6.34
+distribute==0.6.27
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From ab1305d728291f2b4e95019b8ffbc529bef5cd52 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 19:33:32 -0400
Subject: [PATCH 03/13] Going to try rebuilding the environment.

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 6a05826f5..d508a85c0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@ Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
-distribute==0.6.27
+distribute==0.6.28
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From 8f554c51d0c45322539c5c2549a33b3a98f05bb2 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 19:48:17 -0400
Subject: [PATCH 04/13] Try downgrading both distribute and mysql.

---
 requirements.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/requirements.txt b/requirements.txt
index d508a85c0..4bc49b6ff 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,12 +4,12 @@ Flask-Mail==0.9.0
 Flask-Principal==0.4.0
 Jinja2==2.7.1
 MarkupSafe==0.18
-MySQL-python==1.2.4
+MySQL-python==1.2.3
 Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
-distribute==0.6.28
+distribute==0.6.27
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From 705624dd6f60340244bc7f9b89193b77886a6c4d Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 19:55:00 -0400
Subject: [PATCH 05/13] Try install mysql-python through yum.

---
 .ebextensions/python27.config | 1 +
 requirements.txt              | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/.ebextensions/python27.config b/.ebextensions/python27.config
index 0d7b06efb..e474fbea3 100644
--- a/.ebextensions/python27.config
+++ b/.ebextensions/python27.config
@@ -7,6 +7,7 @@ packages:
     python27-devel: []
     make: []
     httpd-devel: []
+    MySQL-python: []
 
 commands:
   # The modwsgi version currently installed on the system
diff --git a/requirements.txt b/requirements.txt
index 4bc49b6ff..d508a85c0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,12 +4,12 @@ Flask-Mail==0.9.0
 Flask-Principal==0.4.0
 Jinja2==2.7.1
 MarkupSafe==0.18
-MySQL-python==1.2.3
+MySQL-python==1.2.4
 Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
-distribute==0.6.27
+distribute==0.6.28
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From 2e3c4723d05af16f574155301abcbaf60463484d Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 20:04:32 -0400
Subject: [PATCH 06/13] I'm losing my mind here. This needs to start working.

---
 .ebextensions/python27.config | 1 -
 requirements.txt              | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/.ebextensions/python27.config b/.ebextensions/python27.config
index e474fbea3..0d7b06efb 100644
--- a/.ebextensions/python27.config
+++ b/.ebextensions/python27.config
@@ -7,7 +7,6 @@ packages:
     python27-devel: []
     make: []
     httpd-devel: []
-    MySQL-python: []
 
 commands:
   # The modwsgi version currently installed on the system
diff --git a/requirements.txt b/requirements.txt
index d508a85c0..71049adb4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,12 +4,11 @@ Flask-Mail==0.9.0
 Flask-Principal==0.4.0
 Jinja2==2.7.1
 MarkupSafe==0.18
-MySQL-python==1.2.4
+MySQL-python==1.2.3
 Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
-distribute==0.6.28
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From f8f86bcb04972673f96c9c8d364713476c8ade18 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 20:13:36 -0400
Subject: [PATCH 07/13] Try to upgrade distribute prior to the app running.

---
 .ebextensions/python27.config | 3 +++
 requirements.txt              | 1 +
 2 files changed, 4 insertions(+)

diff --git a/.ebextensions/python27.config b/.ebextensions/python27.config
index 0d7b06efb..717384315 100644
--- a/.ebextensions/python27.config
+++ b/.ebextensions/python27.config
@@ -23,6 +23,9 @@ commands:
     command: grep "python2.7" 03deploy.py || sed -i 's/--distribute/--python=python2.7 &/' 03deploy.py
     cwd: /opt/elasticbeanstalk/hooks/appdeploy/pre
 
+  updatedistribute:
+    command: easy_install -U distribute
+
 container_commands:
   # Now that our app is installed using python2.7 we
   # need to fix some paths that the wsgi configuration
diff --git a/requirements.txt b/requirements.txt
index 71049adb4..321bcc6c1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,6 +9,7 @@ Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
+distribute==0.6.28
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From 52db1c87961eff6a08c2130d627fe9ce3d26e8f5 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 20:26:41 -0400
Subject: [PATCH 08/13] Try using pymysql instead of mysqldb.

---
 .ebextensions/python27.config | 3 ---
 requirements-nover.txt        | 2 +-
 requirements.txt              | 3 +--
 3 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/.ebextensions/python27.config b/.ebextensions/python27.config
index 717384315..0d7b06efb 100644
--- a/.ebextensions/python27.config
+++ b/.ebextensions/python27.config
@@ -23,9 +23,6 @@ commands:
     command: grep "python2.7" 03deploy.py || sed -i 's/--distribute/--python=python2.7 &/' 03deploy.py
     cwd: /opt/elasticbeanstalk/hooks/appdeploy/pre
 
-  updatedistribute:
-    command: easy_install -U distribute
-
 container_commands:
   # Now that our app is installed using python2.7 we
   # need to fix some paths that the wsgi configuration
diff --git a/requirements-nover.txt b/requirements-nover.txt
index 4f7a3d3b2..9a45a60cd 100644
--- a/requirements-nover.txt
+++ b/requirements-nover.txt
@@ -6,4 +6,4 @@ Flask-Login
 Flask-Mail
 python-dateutil
 boto
-MySQL-python
\ No newline at end of file
+pymysql
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 321bcc6c1..c442ed8c9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,12 +4,11 @@ Flask-Mail==0.9.0
 Flask-Principal==0.4.0
 Jinja2==2.7.1
 MarkupSafe==0.18
-MySQL-python==1.2.3
+PyMySQL==0.5
 Werkzeug==0.9.4
 argparse==1.2.1
 blinker==1.3
 boto==2.13.3
-distribute==0.6.28
 itsdangerous==0.23
 peewee==2.1.4
 py-bcrypt==0.4

From abd4e8a7ab6feaf33be16a4d1cd17245d3d1e934 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 23:18:05 -0400
Subject: [PATCH 09/13] Fix search by removing description as a searched field.

---
 data/model.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/data/model.py b/data/model.py
index c70013b9b..31a432b88 100644
--- a/data/model.py
+++ b/data/model.py
@@ -112,9 +112,7 @@ def get_visible_repositories(username=None):
 def get_matching_repositories(repo_term, username=None):
   visible = get_visible_repositories(username)
   search_clauses = (Repository.name ** ('%' + repo_term + '%') |
-                    Repository.namespace ** ('%' + repo_term + '%') |
-                    Repository.description ** ('%' + repo_term + '%'))
-
+                    Repository.namespace ** ('%' + repo_term + '%'))
 
   final = visible.where(search_clauses).limit(10)
   return list(final)

From 25c4054c1935a54e85b8843765b2a0f270ae1268 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 23:22:52 -0400
Subject: [PATCH 10/13] Long description fields should have been text rather
 than blob.

---
 data/database.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/data/database.py b/data/database.py
index 3a377d02c..6607a4751 100644
--- a/data/database.py
+++ b/data/database.py
@@ -32,7 +32,7 @@ class Repository(BaseModel):
   namespace = CharField()
   name = CharField()
   visibility = ForeignKeyField(Visibility)
-  description = BlobField(null=True)
+  description = TextField(null=True)
 
   class Meta:
     database = db
@@ -89,7 +89,7 @@ class Image(BaseModel):
   image_id = CharField()
   checksum = CharField(null=True)
   created = DateTimeField(null=True)
-  comment = BlobField(null=True)
+  comment = TextField(null=True)
   repository = ForeignKeyField(Repository)
 
   # '/' separated list of ancestory ids, e.g. /1/2/6/7/10/

From 67147240b697df50c4c86c76996e9b9f6d8f583f Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Mon, 30 Sep 2013 23:54:12 -0400
Subject: [PATCH 11/13] Use production config in production and dev config in
 dev.

---
 .ebextensions/python27.config |  4 +++-
 app.py                        |  8 ++++++--
 application.py                |  6 ++----
 config.py                     | 18 +++++++++++++++++-
 4 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/.ebextensions/python27.config b/.ebextensions/python27.config
index 0d7b06efb..4ca890223 100644
--- a/.ebextensions/python27.config
+++ b/.ebextensions/python27.config
@@ -35,4 +35,6 @@ container_commands:
 
 option_settings:
   "aws:elasticbeanstalk:container:python:staticfiles":
-    "/static": "static/"
\ No newline at end of file
+    "/static": "static/"
+  "aws:elasticbeanstalk:application:environment":
+    "STACK": "prod"
diff --git a/app.py b/app.py
index ea52822ee..88a445436 100644
--- a/app.py
+++ b/app.py
@@ -1,13 +1,17 @@
 import logging
+import os
 
 from flask import Flask
 from flask.ext.principal import Principal
 from flask.ext.login import LoginManager
 from flask.ext.mail import Mail
-from config import ProductionConfig
+from config import ProductionConfig, DebugConfig
 
 app = Flask(__name__)
-app.config.from_object(ProductionConfig())
+
+is_prod = os.environ.get('STACK', '').strip().lower().startswith('prod')
+config_object = ProductionConfig() if is_prod else DebugConfig()
+app.config.from_object(config_object)
 
 logger = logging.getLogger(__name__)
 
diff --git a/application.py b/application.py
index c4e4f602e..8dca9ef9c 100644
--- a/application.py
+++ b/application.py
@@ -11,9 +11,7 @@ import endpoints.registry
 # Remove this for prod config
 application.debug = True
 
-if __name__ == '__main__':
-  FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - ' + \
-           '%(funcName)s - %(message)s'
-  logging.basicConfig(format=FORMAT, level=logging.DEBUG)
+logging.basicConfig(**application.config['LOGGING_CONFIG'])
 
+if __name__ == '__main__':
   application.run(port=5001, debug=True)
diff --git a/config.py b/config.py
index b6242b116..a786e8ecc 100644
--- a/config.py
+++ b/config.py
@@ -1,5 +1,12 @@
+import logging
+
 from peewee import MySQLDatabase, SqliteDatabase
 
+
+LOG_FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - ' + \
+             '%(funcName)s - %(message)s'
+
+
 class FlaskConfig(object):
   SECRET_KEY = '1cb18882-6d12-440d-a4cc-b7430fb5f884'
 
@@ -29,7 +36,7 @@ class RDSMySQL(object):
     'host': 'fluxmonkeylogin.cb0vumcygprn.us-east-1.rds.amazonaws.com',
     'user': 'fluxmonkey',
     'passwd': '8eifM#uoZ85xqC^',
-    'threadlocals': True
+    'threadlocals': True,
   }
   DB_DRIVER = MySQLDatabase
 
@@ -48,7 +55,16 @@ class LocalStorage(object):
 
 class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB):
   REGISTRY_SERVER = 'localhost:5000'
+  LOGGING_CONFIG = {
+    'level': logging.DEBUG,
+    'format': LOG_FORMAT
+  }
 
 
 class ProductionConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL):
   REGISTRY_SERVER = 'quay.io'
+  LOGGING_CONFIG = {
+    'filename': '/opt/python/log/application.log',
+    'level': logging.DEBUG,
+    'format': LOG_FORMAT,
+  }

From afb5dbaaa49117a0f8eacbad4853bfb0761bdbdc Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Tue, 1 Oct 2013 00:16:03 -0400
Subject: [PATCH 12/13] Try logging to stderr.

---
 config.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/config.py b/config.py
index a786e8ecc..77240544e 100644
--- a/config.py
+++ b/config.py
@@ -1,4 +1,5 @@
 import logging
+import sys
 
 from peewee import MySQLDatabase, SqliteDatabase
 
@@ -64,7 +65,7 @@ class DebugConfig(FlaskConfig, MailConfig, LocalStorage, SQLiteDB):
 class ProductionConfig(FlaskConfig, MailConfig, S3Storage, RDSMySQL):
   REGISTRY_SERVER = 'quay.io'
   LOGGING_CONFIG = {
-    'filename': '/opt/python/log/application.log',
+    'stream': sys.stderr,
     'level': logging.DEBUG,
     'format': LOG_FORMAT,
   }

From 2a9a8635b685eb073496c5591f0b6e30d9483105 Mon Sep 17 00:00:00 2001
From: yackob03 <jacob.moshenko@gmail.com>
Date: Tue, 1 Oct 2013 00:23:40 -0400
Subject: [PATCH 13/13] We must pass authorization headers to our WSGI app.

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

diff --git a/wsgi.conf b/wsgi.conf
index 0ab627d81..3671e593f 100644
--- a/wsgi.conf
+++ b/wsgi.conf
@@ -3,6 +3,7 @@ LoadModule wsgi_module modules/mod_wsgi.so
 WSGIPythonHome /opt/python/run/venv
 WSGISocketPrefix run/wsgi
 WSGIRestrictEmbedded On
+WSGIPassAuthorization On
 
 <VirtualHost *:80>