def tracked_branch(self) -> Head: """ returns the tracked branch object (of type git.HEAD) or throws if such branch does not exist on the repo """ try: return getattr(self._repo.heads, self._branch_name) except AttributeError as e: branches = [{'name': head.name, 'path': head.path} for head in self._repo.heads] logger.exception("did not find main branch: {error}, instead found: {branches_found}", error=e, branches_found=branches) raise GitFailed(e)
def tracked_remote(self) -> Remote: """ returns the tracked remote object (of type git.Remote) or throws if such branch does not exist on the repo """ try: return getattr(self._repo.remotes, self._remote_name) except AttributeError as e: remotes = [remote.name for remote in self._repo.remotes] logger.exception("did not find main branch: {error}, instead found: {remotes_found}", error=e, remotes_found=remotes) raise GitFailed(e)
async def handle_url(self, url, config): """ Helper function wrapping self._engine.handle_url """ logger.info("Fetching data from url: {url}", url=url) try: # ask the engine to get our data response = await self._engine.handle_url(url, config=config) return response except asyncio.TimeoutError as e: logger.exception("Timeout while fetching url: {url}", url=url) raise
def _attempt_init_from_local_repo(self) -> CloneResult: """ inits the repo from local .git or throws GitFailed """ logger.info("Repo already exists in '{repo_path}'", repo_path=self.path) try: repo = Repo(self.path) except Exception as e: logger.exception("cannot init local repo: {error}", error=e) raise GitFailed(e) return LocalClone(repo)
async def _handle_url(self, url, config): """ Helper function wrapping self._engine.handle_url, returning the fetched result with the url used for it """ logger.info("Fetching data from url: {url}", url=url) try: # ask the engine to get our data response = await self._engine.handle_url(url, config=config) # store as part of all results return url, response except asyncio.TimeoutError as e: logger.exception("Timeout while fetching url: {url}", url=url) raise
def _attempt_clone_from_url(self) -> CloneResult: """ clones the repo from url or throws GitFailed """ env = self._provide_git_ssh_environment() _clone_func = partial(Repo.clone_from, url=self.url, to_path=self.path, env=env) _clone_with_retries = retry(**self._retry_config)(_clone_func) try: repo = _clone_with_retries() except (GitError, GitCommandError) as e: logger.exception("cannot clone policy repo: {error}", error=e) raise GitFailed(e) except RetryError as e: logger.exception("cannot clone policy repo: {error}", error=e) raise GitFailed(e) else: logger.info("Clone succeeded", repo_path=self.path) return RemoteClone(repo)
async def default_server_exception_handler(request: Request, exception: Exception): response = get_response() logger.exception("Uncaught server exception: {exc}", exc=exception) # Since the CORSMiddleware is not executed when an unhandled server exception # occurs, we need to manually set the CORS headers ourselves if we want the FE # to receive a proper JSON 500, opposed to a CORS error. # Setting CORS headers on server errors is a bit of a philosophical topic of # discussion in many frameworks, and it is currently not handled in FastAPI. # See dotnet core for a recent discussion, where ultimately it was # decided to return CORS headers on server failures: # https://github.com/dotnet/aspnetcore/issues/2378 origin = request.headers.get('origin') if origin: # Have the middleware do the heavy lifting for us to parse # all the config, then update our response headers cors = CORSMiddleware( app=app, allow_origins=opal_common_config.ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) # Logic directly from Starlette's CORSMiddleware: # https://github.com/encode/starlette/blob/master/starlette/middleware/cors.py#L152 response.headers.update(cors.simple_headers) has_cookie = "cookie" in request.headers # If request includes any cookie headers, then we must respond # with the specific origin instead of '*'. if cors.allow_all_origins and has_cookie: response.headers["Access-Control-Allow-Origin"] = origin # If we only allow specific origins, then we have to mirror back # the Origin header in the response. elif not cors.allow_all_origins and cors.is_allowed_origin( origin=origin): response.headers["Access-Control-Allow-Origin"] = origin response.headers.add_vary_header("Origin") return response
async def stop_client_background_tasks(self): """ stops all background tasks (called on shutdown event) """ logger.info("stopping background tasks...") # stopping opa runner if self.opa_runner: await self.opa_runner.stop() # stopping updater tasks (each updater runs a pub/sub client) logger.info("trying to shutdown DataUpdater and PolicyUpdater gracefully...") tasks: List[asyncio.Task] = [] if self.data_updater: tasks.append(asyncio.create_task(self.data_updater.stop())) if self.policy_updater: tasks.append(asyncio.create_task(self.policy_updater.stop())) try: await asyncio.gather(*tasks) except Exception: logger.exception("exception while shutting down updaters")