Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade Python packages, switch to FastAPI lifespan async context manager #3765

Merged
merged 33 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4d89e8d
Many Python packages are outdated and need updating
marrobi Oct 25, 2023
4218886
Fix linting
marrobi Oct 25, 2023
8871992
Fix references to core.events
marrobi Oct 25, 2023
210327d
Ignore service bus info logs that are not useful
marrobi Oct 25, 2023
454c720
Remove noisy service bus SDK loggers
marrobi Oct 26, 2023
337fb41
Up dev container AZ cli version
marrobi Nov 6, 2023
7d63f0b
ignore PytestUnraisableExceptionWarning
marrobi Nov 7, 2023
708b1d5
Merge branch 'main' into marrobi/issue3764
marrobi Nov 7, 2023
0bde428
Prevent logging to Azure when no env vars set.
marrobi Nov 7, 2023
c2bfec3
Merge branch 'marrobi/issue3764' of github.com:marrobi/AzureTRE into …
marrobi Nov 7, 2023
0f86076
Fix linting
marrobi Nov 7, 2023
ace4495
Fix airlock service bus background polling
marrobi Nov 7, 2023
d5185d9
fix linting
marrobi Nov 7, 2023
409b02e
linting
marrobi Nov 7, 2023
d8e2e8f
Increase version
marrobi Nov 7, 2023
5c8aeca
Merge branch 'main' into marrobi/issue3764
marrobi Nov 7, 2023
f96716d
Merge branch 'marrobi/issue3764' of github.com:marrobi/AzureTRE into …
marrobi Nov 7, 2023
170c19f
Fix background tasks
marrobi Nov 8, 2023
609d995
fix linting
marrobi Nov 8, 2023
d8111c1
Fix tests
marrobi Nov 8, 2023
2a41f66
Update number of workers and timeout
marrobi Nov 8, 2023
2b402d6
update import
marrobi Nov 8, 2023
df2d4c8
Close event loop in e2e tests
marrobi Nov 8, 2023
d6c79ef
fix lint
marrobi Nov 8, 2023
2bc5e7c
Add console logging
marrobi Nov 8, 2023
eddc6c4
Fixasync background tasks
marrobi Nov 9, 2023
6c2263c
Prevent constant checking.
marrobi Nov 10, 2023
5c069a7
Move azure-storage-blob==12.15.0
marrobi Nov 10, 2023
926f00e
Fix airlock tests
marrobi Nov 13, 2023
d502855
Add dependency to prevent deletion issues
marrobi Nov 13, 2023
76ece86
fix linting and processor issues
marrobi Nov 13, 2023
c6a71f7
Fix tests
marrobi Nov 14, 2023
c761590
fix linting
marrobi Nov 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ RUN export PORTER_VERSION=${PORTER_VERSION} \
ENV PATH ${PORTER_HOME_V1}:$PATH

# Install requirements
ARG PIP_VERSION=23.3.1
RUN pip3 --no-cache-dir install pip==${PIP_VERSION} && pip3 config set global.disable-pip-version-check true
COPY ["requirements.txt", "/tmp/pip-tmp/" ]
COPY ["api_app/requirements.txt", "api_app/requirements-dev.txt", "/tmp/pip-tmp/api_app/" ]
COPY ["resource_processor/vmss_porter/requirements.txt", "/tmp/pip-tmp/resource_processor/vmss_porter/" ]
Expand All @@ -73,7 +75,7 @@ COPY ["airlock_processor/requirements.txt", "/tmp/pip-tmp/airlock_processor/"]
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt

# Install azure-cli
ARG AZURE_CLI_VERSION=2.37.0-1~bullseye
ARG AZURE_CLI_VERSION=2.50.0-1~bullseye
COPY .devcontainer/scripts/azure-cli.sh /tmp/
RUN export AZURE_CLI_VERSION=${AZURE_CLI_VERSION} \
&& /tmp/azure-cli.sh
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ FEATURES:
ENHANCEMENTS:

BUG FIXES:
* Update Python packages, and fix breaking changes ([#3764](https://github.com/microsoft/AzureTRE/issues/3764))
* Enabling support for more than 20 users/groups in Workspace API ([#3759](https://github.com/microsoft/AzureTRE/pull/3759 ))
* Airlock Import Review workspace uses dedicated DNS zone to prevent conflict with core ([#3767](https://github.com/microsoft/AzureTRE/pull/3767))

Expand Down
2 changes: 1 addition & 1 deletion airlock_processor/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.6.1"
__version__ = "0.7.4"
14 changes: 7 additions & 7 deletions airlock_processor/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Do not include azure-functions-worker as it may conflict with the Azure Functions platform
azure-core
azure-functions
azure-storage-blob
azure-identity
azure-mgmt-storage
azure-mgmt-resource
pydantic
azure-core==1.29.5
azure-functions==1.17.0
azure-storage-blob==12.19.0
azure-identity==1.14.1
azure-mgmt-storage==21.1.0
azure-mgmt-resource==23.0.1
pydantic==1.10.13
16 changes: 9 additions & 7 deletions airlock_processor/shared_code/blob_operations.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
import datetime
import logging
import json
import re
from datetime import datetime, timedelta
from typing import Tuple

from azure.core.exceptions import ResourceExistsError
Expand Down Expand Up @@ -69,16 +69,18 @@ def copy_data(source_account_name: str, destination_account_name: str, request_i
logging.error(msg)
raise NoFilesInRequestException(msg)

udk = source_blob_service_client.get_user_delegation_key(datetime.datetime.utcnow() - datetime.timedelta(hours=1),
datetime.datetime.utcnow() + datetime.timedelta(hours=1))

# token geneation with expiry of 1 hour. since its not shared, we can leave it to expire (no need to track/delete)
# Remove sas token if not needed: https://github.com/microsoft/AzureTRE/issues/2034
sas_token = generate_container_sas(account_name=source_account_name,
container_name=container_name,
start = datetime.utcnow() - timedelta(minutes=15)
expiry = datetime.utcnow() + timedelta(hours=1)
udk = source_blob_service_client.get_user_delegation_key(key_start_time=start, key_expiry_time=expiry)

sas_token = generate_container_sas(container_name=container_name,
account_name=source_account_name,
user_delegation_key=udk,
permission=ContainerSasPermissions(read=True),
expiry=datetime.datetime.utcnow() + datetime.timedelta(hours=1))
start=start,
expiry=expiry)

source_blob = source_container_client.get_blob_client(blob_name)
source_url = f'{source_blob.url}?{sas_token}'
Expand Down
2 changes: 1 addition & 1 deletion api_app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ COPY . /api
WORKDIR /api
RUN groupadd -r api && useradd -r -s /bin/false -g api api_user
USER api_user
CMD ["gunicorn", "main:app", "--bind", "0.0.0.0:8000", "-k", "uvicorn.workers.UvicornWorker"]
CMD ["gunicorn", "main:app", "--bind", "0.0.0.0:8000", "-k", "uvicorn.workers.UvicornWorker","--timeout", "60", "--workers", "5"]
2 changes: 1 addition & 1 deletion api_app/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.15.18"
__version__ = "0.16.7"
19 changes: 0 additions & 19 deletions api_app/core/events.py

This file was deleted.

7 changes: 6 additions & 1 deletion api_app/db/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
from azure.cosmos.aio import CosmosClient

from api.dependencies.database import get_db_client
from db.repositories.resources import ResourceRepository
from core import config


async def bootstrap_database(app) -> None:
async def bootstrap_database(app) -> bool:
try:
client: CosmosClient = await get_db_client(app)
if client:
await client.create_database_if_not_exists(id=config.STATE_STORE_DATABASE)
# Test access to database
await ResourceRepository.create(client)
return True
except Exception as e:
logging.debug(e)
return False
2 changes: 1 addition & 1 deletion api_app/db/repositories/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async def _get_container(cls, container_name, partition_key_obj) -> ContainerPro
raise UnableToAccessDatabase

async def query(self, query: str, parameters: Optional[dict] = None):
items = self.container.query_items(query=query, parameters=parameters, enable_cross_partition_query=True)
items = self.container.query_items(query=query, parameters=parameters)
return [i async for i in items]

async def read_item_by_id(self, item_id: str) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion api_app/db/repositories/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,6 @@ async def get_operations_by_resource_id(self, resource_id: str) -> List[Operatio
return parse_obj_as(List[Operation], operations)

async def resource_has_deployed_operation(self, resource_id: str) -> bool:
query = self.operations_query() + f' c.resourceId = "{resource_id}" AND c.action = "{RequestAction.Install}" AND c.status = "{Status.Deployed}"'
query = self.operations_query() + f' c.resourceId = "{resource_id}" AND ((c.action = "{RequestAction.Install}" AND c.status = "{Status.Deployed}") OR (c.action = "{RequestAction.Upgrade}" AND c.status = "{Status.Updated}"))'
operations = await self.query(query=query)
return len(operations) > 0
58 changes: 31 additions & 27 deletions api_app/main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import asyncio
import logging
from opencensus.ext.azure.trace_exporter import AzureExporter
import os
import uvicorn

from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi_utils.tasks import repeat_every
from service_bus.airlock_request_status_update import receive_step_result_message_and_update_status
from fastapi.concurrency import asynccontextmanager

from services.tracing import RequestTracerMiddleware
from opencensus.trace.samplers import ProbabilitySampler
Expand All @@ -20,9 +20,29 @@
from api.errors.validation_error import http422_error_handler
from api.errors.generic_error import generic_error_handler
from core import config
from core.events import create_start_app_handler, create_stop_app_handler
from db.events import bootstrap_database
from services.logging import initialize_logging, telemetry_processor_callback_function
from service_bus.deployment_status_updater import DeploymentStatusUpdater
from service_bus.airlock_request_status_update import AirlockStatusUpdater


@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.cosmos_client = None

while not await bootstrap_database(app):
await asyncio.sleep(5)
logging.warning("Database connection could not be established")

deploymentStatusUpdater = DeploymentStatusUpdater(app)
await deploymentStatusUpdater.init_repos()

airlockStatusUpdater = AirlockStatusUpdater(app)
await airlockStatusUpdater.init_repos()

asyncio.create_task(deploymentStatusUpdater.receive_messages())
asyncio.create_task(airlockStatusUpdater.receive_messages())
yield


def get_application() -> FastAPI:
Expand All @@ -33,16 +53,15 @@ def get_application() -> FastAPI:
version=config.VERSION,
docs_url=None,
redoc_url=None,
openapi_url=None
openapi_url=None,
lifespan=lifespan
)

application.add_event_handler("startup", create_start_app_handler(application))
application.add_event_handler("shutdown", create_stop_app_handler(application))

try:
exporter = AzureExporter(sampler=ProbabilitySampler(1.0))
exporter.add_telemetry_processor(telemetry_processor_callback_function)
application.add_middleware(RequestTracerMiddleware, exporter=exporter)
if os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING"):
exporter = AzureExporter(sampler=ProbabilitySampler(1.0))
exporter.add_telemetry_processor(telemetry_processor_callback_function)
application.add_middleware(RequestTracerMiddleware, exporter=exporter)
except Exception:
logging.exception("Failed to add RequestTracerMiddleware")

Expand All @@ -64,27 +83,12 @@ def get_application() -> FastAPI:


if config.DEBUG:
initialize_logging(logging.DEBUG)
initialize_logging(logging.DEBUG, add_console_handler=True)
else:
initialize_logging(logging.INFO)
initialize_logging(logging.INFO, add_console_handler=False)

app = get_application()


@app.on_event("startup")
async def watch_deployment_status() -> None:
logging.info("Starting deployment status watcher thread")
statusWatcher = DeploymentStatusUpdater(app)
await statusWatcher.init_repos()
current_event_loop = asyncio.get_event_loop()
asyncio.run_coroutine_threadsafe(statusWatcher.receive_messages(), loop=current_event_loop)


@app.on_event("startup")
@repeat_every(seconds=20, wait_first=True, logger=logging.getLogger())
async def update_airlock_request_status() -> None:
await receive_step_result_message_and_update_status(app)


if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000, loop="asyncio")
9 changes: 4 additions & 5 deletions api_app/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Dev requirements
pytest-asyncio==0.20.3
asgi-lifespan~=2.0.0
httpx~=0.23.1
mock==5.0.0
pytest==7.2.0
pytest-asyncio==0.21.1
httpx==0.25.0
mock==5.1.0
pytest==7.4.3
44 changes: 22 additions & 22 deletions api_app/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# API
azure-core==1.26.1
aiohttp==3.8.5
azure-cosmos==4.3.0
azure-identity==1.12.0
azure-mgmt-cosmosdb==9.0.0
azure-mgmt-compute==29.1.0
azure-mgmt-costmanagement==3.0.0
azure-storage-blob==12.15.0
azure-servicebus==7.8.1
azure-eventgrid==4.9.1
fastapi[all]==0.95.0
fastapi-utils==0.2.1
gunicorn==20.1.0
jsonschema[format_nongpl]==4.17.1
msal~=1.20.0
opencensus-ext-azure==1.1.7
azure-core==1.29.5
aiohttp==3.8.6
azure-cosmos==4.5.1
azure-identity==1.14.1
azure-mgmt-cosmosdb==9.3.0
azure-mgmt-compute==30.3.0
azure-mgmt-costmanagement==4.0.1
azure-storage-blob==12.19.0
azure-servicebus==7.11.3
marrobi marked this conversation as resolved.
Show resolved Hide resolved
azure-eventgrid==4.15.0
fastapi==0.104.0
gunicorn==21.2.0
jsonschema[format_nongpl]==4.19.1
msal==1.22.0
opencensus-ext-azure==1.1.11
opencensus-ext-logging==0.1.1
PyJWT==2.6.0
uvicorn[standard]==0.20.0
PyJWT==2.8.0
uvicorn[standard]==0.23.2
semantic-version==2.10.0
pytz~=2022.7
python-dateutil~=2.8.2
azure-mgmt-resource==22.0.0
pandas==1.5.2
pytz==2022.7
python-dateutil==2.8.2
azure-mgmt-resource==23.0.1
pandas==2.0.3
pydantic==1.10.13
2 changes: 1 addition & 1 deletion api_app/run_tests_and_exit_succesfully.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
rm -f ../test-results/pytest_api*
mkdir -p ../test-results

if ! pytest --junit-xml ../test-results/pytest_api_unit.xml --ignore e2e_tests; then
if ! pytest --junit-xml ../test-results/pytest_api_unit.xml --ignore e2e_tests -W ignore::pytest.PytestUnraisableExceptionWarning -W ignore::DeprecationWarning; then
touch ../test-results/pytest_api_unit_failed
fi
Loading
Loading