-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move to using managed identity for auth to CosmosDB. (#3806)
- Loading branch information
Showing
73 changed files
with
488 additions
and
431 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "0.17.1" | ||
__version__ = "0.18.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,86 @@ | ||
from typing import Callable, Type | ||
|
||
from azure.cosmos.aio import CosmosClient | ||
from azure.cosmos.aio import CosmosClient, DatabaseProxy, ContainerProxy | ||
from azure.mgmt.cosmosdb.aio import CosmosDBManagementClient | ||
from fastapi import Depends, FastAPI, HTTPException | ||
from fastapi import Request, status | ||
from core import config, credentials | ||
from db.errors import UnableToAccessDatabase | ||
from db.repositories.base import BaseRepository | ||
from resources import strings | ||
|
||
from core.config import MANAGED_IDENTITY_CLIENT_ID, STATE_STORE_ENDPOINT, STATE_STORE_KEY, STATE_STORE_SSL_VERIFY, SUBSCRIPTION_ID, RESOURCE_MANAGER_ENDPOINT, CREDENTIAL_SCOPES, RESOURCE_GROUP_NAME, COSMOSDB_ACCOUNT_NAME, STATE_STORE_DATABASE | ||
from core.credentials import get_credential_async | ||
from services.logging import logger | ||
|
||
|
||
async def connect_to_db() -> CosmosClient: | ||
logger.debug(f"Connecting to {config.STATE_STORE_ENDPOINT}") | ||
class Singleton(type): | ||
_instances = {} | ||
|
||
def __call__(cls, *args, **kwargs): | ||
if cls not in cls._instances: | ||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) | ||
return cls._instances[cls] | ||
|
||
|
||
try: | ||
async with credentials.get_credential_async() as credential: | ||
primary_master_key = await get_store_key(credential) | ||
class Database(metaclass=Singleton): | ||
|
||
if config.STATE_STORE_SSL_VERIFY: | ||
_cosmos_client: CosmosClient = None | ||
_database_proxy: DatabaseProxy = None | ||
|
||
def __init__(cls): | ||
pass | ||
|
||
@classmethod | ||
async def _connect_to_db(cls) -> CosmosClient: | ||
logger.debug(f"Connecting to {STATE_STORE_ENDPOINT}") | ||
|
||
credential = await get_credential_async() | ||
if MANAGED_IDENTITY_CLIENT_ID: | ||
logger.debug("Connecting with managed identity") | ||
cosmos_client = CosmosClient( | ||
url=config.STATE_STORE_ENDPOINT, credential=primary_master_key | ||
url=STATE_STORE_ENDPOINT, | ||
credential=credential | ||
) | ||
else: | ||
# ignore TLS (setup is a pain) when using local Cosmos emulator. | ||
cosmos_client = CosmosClient( | ||
config.STATE_STORE_ENDPOINT, primary_master_key, connection_verify=False | ||
) | ||
logger.debug("Connecting with key") | ||
primary_master_key = await cls._get_store_key(credential) | ||
|
||
if STATE_STORE_SSL_VERIFY: | ||
logger.debug("Connecting with SSL verification") | ||
cosmos_client = CosmosClient( | ||
url=STATE_STORE_ENDPOINT, | ||
credential=primary_master_key | ||
) | ||
else: | ||
logger.debug("Connecting without SSL verification") | ||
# ignore TLS (setup is a pain) when using local Cosmos emulator. | ||
cosmos_client = CosmosClient( | ||
url=STATE_STORE_ENDPOINT, | ||
credential=primary_master_key, | ||
connection_verify=False | ||
) | ||
logger.debug("Connection established") | ||
return cosmos_client | ||
except Exception: | ||
logger.exception("Connection to state store could not be established.") | ||
|
||
|
||
async def get_store_key(credential) -> str: | ||
if config.STATE_STORE_KEY: | ||
primary_master_key = config.STATE_STORE_KEY | ||
else: | ||
async with CosmosDBManagementClient( | ||
credential, | ||
subscription_id=config.SUBSCRIPTION_ID, | ||
base_url=config.RESOURCE_MANAGER_ENDPOINT, | ||
credential_scopes=config.CREDENTIAL_SCOPES | ||
) as cosmosdb_mng_client: | ||
database_keys = await cosmosdb_mng_client.database_accounts.list_keys( | ||
resource_group_name=config.RESOURCE_GROUP_NAME, | ||
account_name=config.COSMOSDB_ACCOUNT_NAME, | ||
) | ||
primary_master_key = database_keys.primary_master_key | ||
|
||
return primary_master_key | ||
|
||
|
||
async def get_db_client(app: FastAPI) -> CosmosClient: | ||
if not hasattr(app.state, 'cosmos_client') or not app.state.cosmos_client: | ||
app.state.cosmos_client = await connect_to_db() | ||
return app.state.cosmos_client | ||
|
||
@classmethod | ||
async def _get_store_key(cls, credential) -> str: | ||
logger.debug("Getting store key") | ||
if STATE_STORE_KEY: | ||
primary_master_key = STATE_STORE_KEY | ||
else: | ||
async with CosmosDBManagementClient( | ||
credential, | ||
subscription_id=SUBSCRIPTION_ID, | ||
base_url=RESOURCE_MANAGER_ENDPOINT, | ||
credential_scopes=CREDENTIAL_SCOPES | ||
) as cosmosdb_mng_client: | ||
database_keys = await cosmosdb_mng_client.database_accounts.list_keys( | ||
resource_group_name=RESOURCE_GROUP_NAME, | ||
account_name=COSMOSDB_ACCOUNT_NAME, | ||
) | ||
primary_master_key = database_keys.primary_master_key | ||
|
||
async def get_db_client_from_request(request: Request) -> CosmosClient: | ||
return await get_db_client(request.app) | ||
return primary_master_key | ||
|
||
@classmethod | ||
async def get_container_proxy(cls, container_name) -> ContainerProxy: | ||
if cls._cosmos_client is None: | ||
cls._cosmos_client = await cls._connect_to_db() | ||
|
||
def get_repository( | ||
repo_type: Type[BaseRepository], | ||
) -> Callable[[CosmosClient], BaseRepository]: | ||
async def _get_repo( | ||
client: CosmosClient = Depends(get_db_client_from_request), | ||
) -> BaseRepository: | ||
try: | ||
return await repo_type.create(client) | ||
except UnableToAccessDatabase: | ||
logger.exception(strings.STATE_STORE_ENDPOINT_NOT_RESPONDING) | ||
raise HTTPException( | ||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, | ||
detail=strings.STATE_STORE_ENDPOINT_NOT_RESPONDING, | ||
) | ||
if cls._database_proxy is None: | ||
cls._database_proxy = cls._cosmos_client.get_database_client(STATE_STORE_DATABASE) | ||
|
||
return _get_repo | ||
return cls._database_proxy.get_container_client(container_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Callable, Type | ||
|
||
from fastapi import HTTPException, status | ||
|
||
from db.errors import UnableToAccessDatabase | ||
from db.repositories.base import BaseRepository | ||
from resources.strings import UNABLE_TO_GET_STATE_STORE_CLIENT | ||
from services.logging import logger | ||
|
||
|
||
def get_repository(repo_type: Type[BaseRepository],) -> Callable: | ||
async def _get_repo() -> BaseRepository: | ||
try: | ||
return await repo_type.create() | ||
except UnableToAccessDatabase: | ||
logger.exception(UNABLE_TO_GET_STATE_STORE_CLIENT) | ||
raise HTTPException( | ||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, | ||
detail=UNABLE_TO_GET_STATE_STORE_CLIENT, | ||
) | ||
|
||
return _get_repo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.