import logging import os import json from flask import Flask, request, Request, _request_ctx_stack from flask.ext.principal import Principal from flask.ext.login import LoginManager, UserMixin from flask.ext.mail import Mail from werkzeug.routing import BaseConverter import features from avatars.avatars import Avatar from storage import Storage from data import model from data import database from data.userfiles import Userfiles from data.users import UserAuthentication from data.billing import Billing from data.buildlogs import BuildLogs from data.archivedlogs import LogArchive from data.userevent import UserEventsBuilderModule from data.queue import WorkQueue from util.saas.analytics import Analytics from util.saas.exceptionlog import Sentry from util.names import urn_generator from util.config.oauth import GoogleOAuthConfig, GithubOAuthConfig, GitLabOAuthConfig from util.security.signing import Signer from util.saas.queuemetrics import QueueMetrics from util.config.provider import FileConfigProvider, TestConfigProvider from util.config.configutil import generate_secret_key from util.config.superusermanager import SuperUserManager from buildman.jobutil.buildreporter import BuildMetrics OVERRIDE_CONFIG_DIRECTORY = 'conf/stack/' OVERRIDE_CONFIG_YAML_FILENAME = 'conf/stack/config.yaml' OVERRIDE_CONFIG_PY_FILENAME = 'conf/stack/config.py' OVERRIDE_CONFIG_KEY = 'QUAY_OVERRIDE_CONFIG' CONFIG_PROVIDER = FileConfigProvider(OVERRIDE_CONFIG_DIRECTORY, 'config.yaml', 'config.py') app = Flask(__name__) logger = logging.getLogger(__name__) class RegexConverter(BaseConverter): def __init__(self, url_map, *items): super(RegexConverter, self).__init__(url_map) logger.debug('Installing regex converter with regex: %s', items[0]) self.regex = items[0] app.url_map.converters['regex'] = RegexConverter # Instantiate the default configuration (for test or for normal operation). if 'TEST' in os.environ: CONFIG_PROVIDER = TestConfigProvider() from test.testconfig import TestConfig logger.debug('Loading test config.') app.config.from_object(TestConfig()) else: from config import DefaultConfig logger.debug('Loading default config.') app.config.from_object(DefaultConfig()) app.teardown_request(database.close_db_filter) # Load the override config via the provider. CONFIG_PROVIDER.update_app_config(app.config) # Update any configuration found in the override environment variable. OVERRIDE_CONFIG_KEY = 'QUAY_OVERRIDE_CONFIG' environ_config = json.loads(os.environ.get(OVERRIDE_CONFIG_KEY, '{}')) app.config.update(environ_config) class RequestWithId(Request): request_gen = staticmethod(urn_generator(['request'])) def __init__(self, *args, **kwargs): super(RequestWithId, self).__init__(*args, **kwargs) self.request_id = self.request_gen() @app.before_request def _request_start(): logger.debug('Starting request: %s', request.path) @app.after_request def _request_end(r): logger.debug('Ending request: %s', request.path) return r class InjectingFilter(logging.Filter): def filter(self, record): if _request_ctx_stack.top is not None: record.msg = '[%s] %s' % (request.request_id, record.msg) return True # Add the request id filter to all handlers of the root logger for handler in logging.getLogger().handlers: handler.addFilter(InjectingFilter()) app.request_class = RequestWithId features.import_features(app.config) Principal(app, use_sessions=False) avatar = Avatar(app) login_manager = LoginManager(app) mail = Mail(app) storage = Storage(app) userfiles = Userfiles(app, storage) log_archive = LogArchive(app, storage) analytics = Analytics(app) billing = Billing(app) sentry = Sentry(app) build_logs = BuildLogs(app) authentication = UserAuthentication(app, OVERRIDE_CONFIG_DIRECTORY) userevents = UserEventsBuilderModule(app) superusers = SuperUserManager(app) signer = Signer(app, OVERRIDE_CONFIG_DIRECTORY) queue_metrics = QueueMetrics(app) build_metrics = BuildMetrics(app) tf = app.config['DB_TRANSACTION_FACTORY'] github_login = GithubOAuthConfig(app.config, 'GITHUB_LOGIN_CONFIG') github_trigger = GithubOAuthConfig(app.config, 'GITHUB_TRIGGER_CONFIG') gitlab_trigger = GitLabOAuthConfig(app.config, 'GITLAB_TRIGGER_CONFIG') google_login = GoogleOAuthConfig(app.config, 'GOOGLE_LOGIN_CONFIG') oauth_apps = [github_login, github_trigger, gitlab_trigger, google_login] image_diff_queue = WorkQueue(app.config['DIFFS_QUEUE_NAME'], tf) dockerfile_build_queue = WorkQueue(app.config['DOCKERFILE_BUILD_QUEUE_NAME'], tf, reporter=queue_metrics.report) notification_queue = WorkQueue(app.config['NOTIFICATION_QUEUE_NAME'], tf) database.configure(app.config) model.config.app_config = app.config model.config.store = storage # Generate a secret key if none was specified. if app.config['SECRET_KEY'] is None: logger.debug('Generating in-memory secret key') app.config['SECRET_KEY'] = generate_secret_key() @login_manager.user_loader def load_user(user_uuid): logger.debug('User loader loading deferred user with uuid: %s' % user_uuid) return LoginWrappedDBUser(user_uuid) class LoginWrappedDBUser(UserMixin): def __init__(self, user_uuid, db_user=None): self._uuid = user_uuid self._db_user = db_user def db_user(self): if not self._db_user: self._db_user = model.user.get_user_by_uuid(self._uuid) return self._db_user def is_authenticated(self): return self.db_user() is not None def is_active(self): return self.db_user().verified def get_id(self): return unicode(self._uuid) def get_app_url(): return '%s://%s' % (app.config['PREFERRED_URL_SCHEME'], app.config['SERVER_HOSTNAME'])