def init_app() -> FastAPI:
    # Warm the sources once before starting
    sources_refresh()
    LOG.info('Initial fetch of Sources completed')

    application = FastAPI(
        title='Sovereign',
        version=__versionstr__,
        debug=config.debug_enabled
    )
    application.include_router(discovery.router, tags=['Configuration Discovery'], prefix='/v2')
    application.include_router(crypto.router, tags=['Cryptographic Utilities'], prefix='/crypto')
    application.include_router(admin.router, tags=['Debugging Endpoints'], prefix='/admin')
    application.include_router(interface.router, tags=['User Interface'], prefix='/ui')
    application.include_router(healthchecks.router, tags=['Healthchecks'])

    application.add_middleware(RequestContextLogMiddleware)
    application.add_middleware(LoggingMiddleware)
    application.add_middleware(ScheduledTasksMiddleware)

    if config.sentry_dsn and sentry_sdk:
        sentry_sdk.init(config.sentry_dsn)
        application.add_middleware(SentryAsgiMiddleware)
        LOG.info('Sentry middleware enabled')

    @application.exception_handler(500)
    async def exception_handler(_, exc: Exception) -> json_response_class:
        """
        We cannot incur the execution of this function from unit tests
        because the starlette test client simply returns exceptions and does
        not run them through the exception handler.
        Ergo, this is a facade function for `generic_error_response`
        """
        return generic_error_response(exc)  # pragma: no cover

    @application.get('/')
    def redirect_to_docs():
        return RedirectResponse('/ui')

    @application.get('/static/{filename}', summary='Return a static asset')
    def static(filename: str):
        return FileResponse(resource_filename('sovereign', f'static/{filename}'))

    return application
Example #2
0
 async def dispatch(self, request: Request,
                    call_next: RequestResponseEndpoint):
     start_time = time.time()
     response = Response("Internal server error", status_code=500)
     new_log_context()
     add_log_context(env=config.environment,
                     site=request.headers.get('host', '-'),
                     method=request.method,
                     uri_path=request.url.path,
                     uri_query=dict(request.query_params.items()),
                     src_ip=request.client.host,
                     src_port=request.client.port,
                     pid=os.getpid(),
                     user_agent=request.headers.get('user-agent', '-'),
                     bytes_in=request.headers.get('content-length', '-'))
     try:
         response: Response = await call_next(request)
     finally:
         duration = time.time() - start_time
         LOG.info(bytes_out=response.headers.get('content-length', '-'),
                  status=response.status_code,
                  duration=duration,
                  request_id=response.headers.get('X-Request-Id', '-'))
         if 'discovery' in str(request.url):
             tags = {
                 'path':
                 request.url.path,
                 'xds_type':
                 response.headers.get("X-Sovereign-Requested-Type"),
                 'client_version':
                 response.headers.get("X-Sovereign-Client-Build"),
                 'response_code':
                 response.status_code,
             }
             tags = [
                 ':'.join(map(str, [k, v])) for k, v in tags.items()
                 if v is not None
             ]
             stats.increment('discovery.rq_total', tags=tags)
             stats.timing('discovery.rq_ms',
                          value=duration * 1000,
                          tags=tags)
     return response
        application.add_middleware(SentryAsgiMiddleware)
        LOG.info('Sentry middleware enabled')

    @application.exception_handler(500)
    async def exception_handler(_, exc: Exception) -> json_response_class:
        """
        We cannot incur the execution of this function from unit tests
        because the starlette test client simply returns exceptions and does
        not run them through the exception handler.
        Ergo, this is a facade function for `generic_error_response`
        """
        return generic_error_response(exc)  # pragma: no cover

    @application.get('/')
    def redirect_to_docs():
        return RedirectResponse('/ui')

    @application.get('/static/{filename}', summary='Return a static asset')
    def static(filename: str):
        return FileResponse(resource_filename('sovereign', f'static/{filename}'))

    return application


app = init_app()
LOG.info(f'Sovereign started and listening on {asgi_config.host}:{asgi_config.port}')


if __name__ == '__main__':  # pragma: no cover
    uvicorn.run(app, host='0.0.0.0', port=8000, access_log=False)