-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #261 from RDFLib/function_app_patches
Update Azure Function app files
- Loading branch information
Showing
11 changed files
with
202 additions
and
11 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ venv/ | |
.vscode/ | ||
.idea/ | ||
.git/ | ||
build/ | ||
test_*.py | ||
.github/ | ||
Dockerfile | ||
|
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 |
---|---|---|
|
@@ -6,6 +6,7 @@ __pycache__/ | |
.pytest_cache/ | ||
.env* | ||
dist/ | ||
build/ | ||
!.env-template | ||
rdf/ | ||
http/ | ||
|
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,7 @@ | ||
.vscode/ | ||
.venv/ | ||
.idea/ | ||
__pycache__/ | ||
__pycache__ | ||
local.settings.json | ||
|
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,39 @@ | ||
# Prez Azure Function-App deployment files | ||
|
||
This directory contains the files required to build and start or publish Prez as an Azure Function-App, as well as a Dockerfile that | ||
can be used to build a container image for deploying the app as an Azure Container App. | ||
|
||
## Publishing | ||
There is a publish_or_start.sh script that can be used to either build and run the function app locally, or publish the app to Azure. | ||
To call it, make sure you are not in the "azure" directory, instead run the script from the root of the project. | ||
|
||
```bash | ||
./azure/publish_or_start.sh start|publish <function-app-name> --extra-options | ||
``` | ||
The FunctionAppName is required for publishing only, and is the name of the Azure Function-App that you want to publish to. | ||
Note, the FunctionAppName must be the second argument to the script, after any optional arguments. | ||
|
||
This script will perform the following steps: | ||
1. Create a ./build directory | ||
2. Copy the required azure function files from the ./azure directory into the ./build directory | ||
* ./azure/function_app.py | ||
* ./azure/patched_asgi_function_wrapper.py | ||
* ./azure/host.json | ||
* ./azure/.funcignore | ||
3. Copy the local prez module source code into the ./build directory | ||
4. Copy the .env file into the ./build directory if it exists | ||
5. Copy the pyproject.toml and poetry.lock files into the ./build directory | ||
6. Generate the requirements.txt file using poetry | ||
7. Start the app locally, or publish the app to the Azure Function-App (using remote build) | ||
|
||
**extra-options** can be used to pass additional arguments to the azure publish command. (Eg, the `--subscription` argument) | ||
|
||
_Note:_ the script automatically adds the `--build remote` argument to the publish command, you don't need to specify it. | ||
|
||
## Building the Docker container image | ||
|
||
To build the Docker container image, run the following command from the root of the project: | ||
|
||
```bash | ||
docker build -t <image-name> -f azure/azure_functions.Dockerfile . | ||
``` |
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
File renamed without changes.
File renamed without changes.
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,63 @@ | ||
from typing import Union, TYPE_CHECKING | ||
from copy import copy | ||
import azure.functions as func | ||
from azure.functions.decorators.http import HttpMethod | ||
from azure.functions._http_asgi import AsgiMiddleware, AsgiRequest, AsgiResponse | ||
from azure.functions._http_wsgi import WsgiMiddleware | ||
from azure.functions._abc import Context | ||
from azure.functions import HttpRequest | ||
|
||
# ------------------- | ||
# Create a patched AsgiFunctionApp to fix the ASGI scope state issue | ||
# ------------------- | ||
# See https://github.com/Azure/azure-functions-python-worker/issues/1566 | ||
class MyAsgiMiddleware(AsgiMiddleware): | ||
async def _handle_async(self, req, context): | ||
asgi_request = AsgiRequest(req, context) | ||
scope = asgi_request.to_asgi_http_scope() | ||
# shallow copy the state as-per the ASGI spec | ||
scope["state"] = copy(self.state) # <-- this is the patch, add the state to the scope | ||
asgi_response = await AsgiResponse.from_app(self._app, | ||
scope, | ||
req.get_body()) | ||
return asgi_response.to_func_response() | ||
|
||
# ------------------- | ||
# Create a patched AsgiFunctionApp to fix the double-slash route issue | ||
# ------------------- | ||
# See https://github.com/Azure/azure-functions-python-worker/issues/1310 | ||
class AsgiFunctionApp(func.AsgiFunctionApp): | ||
def __init__(self, app, http_auth_level): | ||
super(AsgiFunctionApp, self).__init__(None, http_auth_level=http_auth_level) | ||
self._function_builders.clear() | ||
self.middleware = MyAsgiMiddleware(app) | ||
self._add_http_app(self.middleware) | ||
self.startup_task_done = False | ||
|
||
def _add_http_app( | ||
self, http_middleware: Union[AsgiMiddleware, WsgiMiddleware] | ||
) -> None: | ||
"""Add an Asgi app integrated http function. | ||
:param http_middleware: :class:`WsgiMiddleware` | ||
or class:`AsgiMiddleware` instance. | ||
:return: None | ||
""" | ||
|
||
asgi_middleware: AsgiMiddleware = http_middleware | ||
|
||
@self.http_type(http_type="asgi") | ||
@self.route( | ||
methods=(method for method in HttpMethod), | ||
auth_level=self.auth_level, | ||
route="{*route}", # <-- this is the patch, removed the leading slash from the route | ||
) | ||
async def http_app_func(req: HttpRequest, context: Context): | ||
if not self.startup_task_done: | ||
success = await asgi_middleware.notify_startup() | ||
if not success: | ||
raise RuntimeError("ASGI middleware startup failed.") | ||
self.startup_task_done = True | ||
|
||
return await asgi_middleware.handle_async(req, context) |
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,67 @@ | ||
#!/bin/bash | ||
DEFAULT_FUNC=$(which func) | ||
DEFAULT_POETRY=$(which poetry) | ||
FUNC_CLI=${FUNC_CLI:-"$DEFAULT_FUNC"} | ||
POETRY=${POETRY:-"$DEFAULT_POETRY"} | ||
|
||
if [[ "$#" -lt 1 ]] ; then | ||
echo "Usage: $0 <start|publish> [optional arguments] [FunctionAppName]" | ||
echo " start: Run the function app locally (FunctionAppName not required)" | ||
echo " publish: Publish the function app to Azure (FunctionAppName required)" | ||
exit 1 | ||
fi | ||
|
||
# Extract the first argument as the ACTION | ||
ACTION="$1" | ||
shift | ||
|
||
CWD="$(pwd)" | ||
BASE_CWD="${CWD##*/}" | ||
if [[ "$BASE_CWD" = "azure" ]] ; then | ||
echo "Do not run this script from within the azure directory" | ||
echo "Run from the root of the repo" | ||
echo "eg: ./azure/publish_or_start.sh start" | ||
exit 1 | ||
fi | ||
|
||
if [[ -z "$FUNC_CLI" ]] ; then | ||
echo "func cli not found, specify the location using env FUNC_CLI" | ||
exit 1 | ||
fi | ||
|
||
if [[ -z "$POETRY" ]] ; then | ||
echo "poetry not found. Local poetry>=1.8.2 is required to generate the requirements.txt file" | ||
echo "specify the location using env POETRY" | ||
exit 1 | ||
fi | ||
|
||
mkdir -p build | ||
rm -rf build/* | ||
cp ./azure/function_app.py ./azure/patched_asgi_function_wrapper.py ./azure/.funcignore ./azure/host.json ./azure/local.settings.json build/ | ||
cp ./pyproject.toml ./poetry.lock ./build | ||
cp -r ./prez ./build | ||
if [[ -f "./.env" ]] ; then | ||
cp ./.env ./build | ||
fi | ||
cd ./build | ||
"$POETRY" export --without-hashes --format=requirements.txt > requirements.txt | ||
echo "generated requirements.txt" | ||
cat ./requirements.txt | ||
|
||
if [[ "$ACTION" == "publish" ]] ; then | ||
if [[ "$#" -lt 1 ]] ; then | ||
echo "Error: FunctionAppName is required for publish action" | ||
exit 1 | ||
fi | ||
FUNC_APP_NAME="$1" | ||
shift | ||
"$FUNC_CLI" azure functionapp publish "$FUNC_APP_NAME" --build remote "$@" | ||
elif [[ "$ACTION" == "start" ]] ; then | ||
"$FUNC_CLI" start "$@" | ||
else | ||
echo "Invalid action. Use 'start' for local testing or 'publish' for publishing to Azure." | ||
exit 1 | ||
fi | ||
|
||
cd .. | ||
echo "You can now delete the build directory if you wish." |
This file was deleted.
Oops, something went wrong.