kuasha/cosmos

View on GitHub
samples/barebone/cosmosmain.py

Summary

Maintainability
A
2 hrs
Test Coverage
"""
 Copyright (C) 2014 Maruf Maniruzzaman
 Website: http://cosmosframework.com
 Author: Maruf Maniruzzaman
 License :: OSI Approved :: MIT License
"""

import sys
import os

try:
    import endpoints
    import settings
except ImportError as ie:
    sys.path.append(os.path.dirname(os.path.realpath(__file__)))
    import settings
    import endpoints

import importlib

import signal
import imp
import motor
from pymongo import MongoClient
import gridfs

from cosmos.admin.commands import CommandHandler
from cosmos.common.constants import *
import cosmos.datamonitor.monitor
from cosmos.rbac.service import RbacService
import cosmos.service.servicemain

from cosmos.service import *
from cosmos.service.utils import *
import cosmos.datamonitor.monitor as monitor


monitor_worker = None
db = None


def init_database(options):
    client = motor.MotorClient(options.db_uri)
    db = client[options.db_name]

    loader = cosmos.service.BootLoader()

    if options.config_log:
        loader.config_mongolog(options.log_db_uri, options.log_db_name, options.log_col_name,  options.log_level)
    else:
        logging.getLogger().setLevel(options.log_level)

    return db


def cleanup():
    monitor.continue_monitor = False

def int_signal_handler(signal, frame):
    logging.info('Exiting...')
    cleanup()
    sys.exit(0)


# This function will be called in the context of monitor worker thread, NOT from the thread __main__ below is running.
def end_monitor_callback(reason=None):
    pass


def start_monitor(options):
    monitor.continue_monitor = True
    monitor.start_object_change_monitor(options.db_processor_endpoint, options.db_uri, end_monitor_callback)


def start_service(options):
    cosmos.service.servicemain.start_web_service(options)


def load_python_module(fullname, code):
    py_module = imp.new_module(fullname)
    exec(code in py_module.__dict__)
    sys.modules[fullname] = py_module
    return py_module

def get_grid_file_content(db, file_id):
    fs = gridfs.GridFS(db)
    _id = ObjectId(file_id)
    return fs.get(_id).read()

#TODO: add signing mechanism to avoid loading untrusted code
def load_source_module(db, source_module):
    module_name = source_module.get("fullname")
    module_type = source_module.get("type")
    try:
        print("Loading source module " + module_name + " " + module_type + "\n")
        print( "--------------------------------------\n")
        source_code = None

        if module_type == COSMOS_SOURCE_MODULES_TYPE_EMBEDDED:
            source_code = source_module.get("code")

        if module_type == COSMOS_SOURCE_MODULES_TYPE_GRIDFILE:
            file_id = source_module.get("file_id")
            source_code = get_grid_file_content(db, file_id)

        if(source_code):
            print(source_code)
            load_python_module(module_name, source_code)
    except Exception as ex:
        print("Could not load source module " + str(module_name) +": " + str(ex))

    print("--------------------------------------\n")


def load_source_modules(db):
    collection_name = COSMOS_SOURCE_MODULES_OBJECT_NAME
    source_modules = []

    cursor = db[collection_name].find()
    for source_module in cursor:
        try:
            source_modules.append(source_module)
        except Exception as ex:
            print("Unable to load app request handler." + str(ex))

    return source_modules


def get_sync_db(db_uri, db_name):
    client = MongoClient(db_uri)
    return client[db_name]


def load_app_endpoints(db):
    collection_name = COSMOS_APPLICATION_ENDPOINT_LIST_OBJECT_NAME
    app_enfpoints = []

    cursor = db[collection_name].find()
    for endpoint_def in cursor:
        try:
            print("Loading " + endpoint_def["handler_module"] + "." +endpoint_def["handler_name"])
            app_module = importlib.import_module(endpoint_def["handler_module"])
            globals().update(app_module.__dict__)
            handler_func = getattr(app_module, endpoint_def["handler_name"])
            app_enfpoints.append((str(endpoint_def["uri_pattern"]), handler_func))
        except Exception as ex:
            print("Unable to load app request handler." + str(ex))

    return app_enfpoints


def load_interceptors(db):
    collection_name = COSMOS_INTERCEPTOR_OBJECT_NAME
    interceptors = []

    cursor = db[collection_name].find()
    for interceptor_def in cursor:
        try:
            print("Loading interceptor" + interceptor_def["interceptor_module"] + "." + interceptor_def["interceptor_name"])

            app_module = importlib.import_module(interceptor_def["interceptor_module"])
            interceptor_func = getattr(app_module, interceptor_def["interceptor_name"])

            access = interceptor_def["access"]
            interceptor_type = interceptor_def["interceptor_type"]
            object_name = interceptor_def["object_name"]

            interceptors.append(
                {
                    "object_name": object_name,
                    "function": interceptor_func,
                    "access": access,
                    "type": interceptor_type
                }
            )
        except Exception as ex:
            print("Unable to load interceptor." + str(ex))

    return interceptors


def get_options(sync_db, port):
    source_root = os.path.dirname(os.path.realpath(__file__))
    options = Options(**dict(
        db_uri=settings.DATABASE_URI,
        db_name=settings.DB_NAME,
        log_db_uri=settings.LOG_DATABASE_URI,
        log_db_name=settings.LOG_DB_NAME,
        log_col_name=settings.LOG_COL_NAME,
        log_level=settings.LOG_LEVEL,
        config_log=settings.CONFIGURE_LOG,
        db_processor_endpoint=settings.DB_CHANGE_PROCESSOR_ENDPOINT_FORMAT.format(port),
        observers=settings.observers,
        web_service_port=port,
        login_url=settings.login_url,
        cookie_secret=settings.COOKIE_SECRET,
        hmac_key=settings.HMAC_KEY,
        xheaders=True,
        template_path=settings.TEMPLATE_PATH,
        debug=settings.DEBUG,
        default_login_next_uri=settings.DEFAULT_LOGIN_NEXT_URI,
        facebook_api_key=settings.facebook_client_id,
        facebook_secret=settings.facebook_client_secret,
        facebook_scope=settings.facebook_scope,
        facebook_redirect_uri=settings.facebook_redirect_uri,
        google_oauth=settings.GOOGLE_OAUTH2_SETTINGS,
        github_oauth=settings.GITHUB_OAUTH_SETTINGS,
        start_db_monitor=settings.START_OBJECT_CHANGE_MONITOR,
        start_web_service=settings.START_WEB_SERVER,
        oauth2_settings={
            'oauth2_service_url': settings.OAUTH2_SERVICE_URL,
            'oauth2_private_key_pem': settings.OAUTH2_PRIVATE_KEY_PEM,
            'oauth2_public_key_pem': settings.OAUTH2_PUBLIC_KEY_PEM,
            'oauth2_token_expiry_seconds': settings.OAUTH2_TOKEN_EXPIRY_SECONDS,
            'oauth2_trusted_redirect_urls': settings.OAUTH2_TRUSTED_REDIRECT_URLS
        },
        directory_listing_allowed=settings.directory_listing_allowed,
        source_root=source_root,
        tenant_id=settings.TENANT_ID,
    ))

    options.db = init_database(options)

    app_enfpoints = load_app_endpoints(sync_db)
    options.endpoints = app_enfpoints + endpoints.END_POINTS

    return options


def prepare(port):
        sync_db = get_sync_db(settings.DATABASE_URI, settings.DB_NAME)

        print("Loading source modules")
        source_modules = load_source_modules(sync_db)
        for source_module in source_modules:
            load_source_module(sync_db, source_module)

        options = get_options(sync_db, port)

        db_observers = load_interceptors(sync_db)

        options.observers = db_observers + settings.observers

        return options


def main():
    current_directory = os.getcwd()
    print ("Python version: " + str(sys.version_info))
    print ("Running from directory: " + current_directory)

    port = settings.WEB_SERVER_LISTEN_PORT

    if len(sys.argv) < 2:
        command = "start-service"
    else:
        command = sys.argv[1].strip()

    if len(sys.argv) >= 3:
        port = int(sys.argv[2].strip())

    options = prepare(port)

    if command == "start-service":

        if options.start_web_service:
            start_service(options)
        if options.start_db_monitor:
           start_monitor(options)

        signal.signal(signal.SIGINT, int_signal_handler)
        tornado.ioloop.IOLoop.instance().start()
    else:
        cosmos.admin.commands.admin_main()

if __name__ == "__main__":
    main()