* fix linting

* update documentation

* fix docker build

* fix linting

* fix linting

* fix docker workflow

* update app gateway path_rule to reflect that api is now at /api

* add probe per marrobis request

* fix markdown

* save files before line endings

* Normalize line endings

* update code project structure

* fix spelling nits

* fix per sachins comments

Co-authored-by: Tess Ferrandez <tess.ferrandez@microsoft.com>
This commit is contained in:
Tess Ferrandez 2021-05-24 13:23:00 +02:00 коммит произвёл GitHub
Родитель 6b8ffc8a9e
Коммит 0fac2eddab
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
58 изменённых файлов: 531 добавлений и 188 удалений

Просмотреть файл

@ -0,0 +1,2 @@
[flake8]
ignore = E501

39
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,39 @@
# These settings are for any web project
# Handle line endings automatically for files detected as text
# and leave all files detected as binary untouched.
* text=auto
# Force the following filetypes to have unix eols, so Windows does not break them
*.* text eol=lf
# Windows forced line-endings
/.idea/* text eol=crlf
#
## These files are binary and should be left untouched
#
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary

2
.github/linters/.flake8 поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
[flake8]
ignore = E501

Просмотреть файл

@ -3,11 +3,11 @@ name: Docker build and push
on:
pull_request:
paths:
- core/api/**
- management_api_app/**
push:
branches: [ develop, main ]
paths:
- core/api/**
- management_api_app/**
workflow_dispatch:
jobs:
@ -39,5 +39,5 @@ jobs:
TAG=$GITHUB_SHA
fi
docker build -t ${{ env.REPOSITORY_NAME}}:$TAG core/api/
docker build -t ${{ env.REPOSITORY_NAME}}:$TAG core/
docker push ${{ env.REPOSITORY_NAME}}:$TAG

32
.github/workflows/deploy_api.yml поставляемый
Просмотреть файл

@ -1,32 +0,0 @@
name: Deploy API to Azure App Service
on:
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: Dev
steps:
- uses: actions/checkout@v2
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy Web App
uses: azure/CLI@v1
with:
azcliversion: 2.20.0
inlineScript: |
cd core/api
az webapp up --sku S1 -n TREAPIService -g TRERG --location westeurope
- name: Set startup command for Web App
uses: azure/CLI@v1
with:
azcliversion: 2.20.0
inlineScript: |
az webapp config set -n TREAPIService -g TRERG --startup-file='gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app'

2
.gitignore поставляемый
Просмотреть файл

@ -104,7 +104,7 @@ celerybeat.pid
*.sage.py
# Environments
.env
**/.env
.venv
env/
venv/

Просмотреть файл

@ -4,6 +4,7 @@
"**/*devcontainer*",
".git",
".gitignore",
".gitattributes",
"node_modules",
"LICENSE",
"**/.dockerignore",
@ -53,7 +54,11 @@
"tfvars",
"tmpl",
"TREs",
"Xamarin"
"Xamarin",
"devcontainer",
"DevContainer",
"endpoints",
"pytest"
],
"ignoreText": [
// Code sections (anything between ``` and ``` or ` and `)

Просмотреть файл

@ -1,15 +1,15 @@
# Support
## How to file issues and get help
This project has been created to accelerate the development of Trusted Research Environments by Microsoft and its partners while working with customers.
This project does not have a dedicated team of maintainers outside of those who are working on an active engagements and as such issues will be responded to on a best efforts basis. No guarantees can be offered as to response times on issues, feature requests, or to the long term road map for the project.
Taking the above statement into account the team involved in this effort wish for the project to evolve into a stable, production ready resource with active contributors from Microsoft and the wider community.
This project uses GitHub Issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new Issue.
## Microsoft Support Policy
Support for this project is limited to the resources listed above.
# Support
## How to file issues and get help
This project has been created to accelerate the development of Trusted Research Environments by Microsoft and its partners while working with customers.
This project does not have a dedicated team of maintainers outside of those who are working on an active engagements and as such issues will be responded to on a best efforts basis. No guarantees can be offered as to response times on issues, feature requests, or to the long term road map for the project.
Taking the above statement into account the team involved in this effort wish for the project to evolve into a stable, production ready resource with active contributors from Microsoft and the wider community.
This project uses GitHub Issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new Issue.
## Microsoft Support Policy
Support for this project is limited to the resources listed above.

Просмотреть файл

@ -1,67 +0,0 @@
# Core Applications
## TRE API
### Endpoints
#### /ping
- Check if API is alive
### Run locally
Install the requirements on the dev machine
```cmd
virtualenv venv
source venv/bin/activate # linux
venv/Scripts/activate # windows
pip install -r core/api/requirements.txt
```
Run the web server locally
```cmd
cd core
uvicorn api.main:app
```
This will start the API on [http://127.0.0.1:8000](http://127.0.0.1:8000) and you can interact with the swagger on [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
Run the API Tests locally
```cmd
pytest
```
### Build and run in docker
From the root of the repository, build the container image:
```cmd
cd core/api
docker build -t tre-management-api .
```
To run:
```cmd
docker run -it -p 8000:8000 tre-management-api
```
This will start the API on [http://127.0.0.1:8000](http://127.0.0.1:8000) and you can interact with the swagger on [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
### Deploy manually to Azure App Service
- Install [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
- Login to Azure with your credentials `az login`
- Change the directory to api `cd api`
- Deploy to Azure App Service `az webapp up --sku S1 -n MyUniqueAppName -g MyResourceGroup -l westeurope` This will create a Resource Group, App Service Plan and App Service if they do not exist. If App Service exists, it will deploy the solution to it.
- Configure App Service startup command `az webapp config set --startup-file='gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app'`
### Setup CI/CD deployment to Azure App Service
- Install [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
- Login to Azure with your credentials `az login`
- Create a resource group that you want to automatically deploy the solution to `az group create -g MyResourceGroup -l westeurope`
- Create a service credential to run the pipeline with `az ad sp create-for-rbac --name MySPNName --role Contributor --scope /subscriptions/{MySubscriptionId}/resourceGroups/{MyResourceGroup} --sdk-auth`
- In your repository, use Add secret to create a new secret named AZURE_CREDENTIALS and paste the entire JSON object produced by the `az ad sp create-for-rbac` command as the secret value and save the secret.

Просмотреть файл

@ -1,11 +0,0 @@
from fastapi import FastAPI
from routers import ping
app = FastAPI()
app.include_router(ping.router)
@app.get('/')
async def home():
return "Welcome to the TRE API"

Просмотреть файл

@ -1,4 +0,0 @@
Click==7.1.2
fastapi[all]==0.63.0
uvicorn[standard]==0.13.4
gunicorn==20.1.0

Просмотреть файл

@ -1,9 +0,0 @@
from fastapi import APIRouter
router = APIRouter(prefix="/ping", tags=["ping"])
@router.get("/")
async def ping():
return "pong"

Просмотреть файл

@ -1,6 +0,0 @@
import os
import sys
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir, 'api'))
sys.path.insert(0, PROJECT_DIR)

Просмотреть файл

@ -1,11 +0,0 @@
from fastapi.testclient import TestClient
from api.main import app
client = TestClient(app)
def test_ping():
response = client.get("/ping")
assert response.status_code == 200
assert response.json() == "pong"

Просмотреть файл

@ -0,0 +1,99 @@
# Developer Setup
- [Develop and run locally on Windows](#develop-and-run-locally-on-windows)
- [Develop and run in a DevContainer](#develop-and-run-in-a-devcontainer)
- [Deploy with docker](#deploy-with-docker)
- [Run tests](#run-tests)
- [(Optional) Install pre-commit hooks](#optional-install-pre-commit-hooks)
- [API Endpoints](#api-endpoints)
- [Project Structure](#project-structure)
## Develop and run locally on Windows
1. [Install the Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator?tabs=cli%2Cssl-netstd21)
1. Install python dependencies (in a virtual environment)
```cmd
virtualenv venv
venv/Scripts/activate
pip install -r requirements.txt
```
1. Copy `.env.tmpl` in the **core** folder to `.env` and configure the variables
1. Start the web API
```cmd
cd management_api_app
uvicorn main:app --reload
```
The API will be available at [https://localhost:8000/api](https://localhost:8000/api) in your browser.
## Develop and run in a DevContainer
1. [Create a Cosmos DB Database in Azure](https://docs.microsoft.com/en-us/azure/cosmos-db/create-cosmosdb-resources-portal)
1. Open the project in Visual Studio Code in the DevContainer
1. Copy `.env.tmpl` in the **core** folder to `.env` and configure the variables
1. Start the web API
```cmd
cd core
uvicorn main:app --reload
```
The API will be available at [https://localhost:8000/api](https://localhost:8000/api) in your browser.
## Deploy with docker
You must have docker and docker-compose tools installed, and an Azure Cosmos DB configured in `.env` as described above.
Then run:
```cmd
cd core
docker-compose up -d app
```
The API will be available at [https://localhost:8000/api](https://localhost:8000/api) in your browser.
## Run tests
Tests are written with pytest and located in the `tests` folder
Run all tests with:
```cmd
pytest
```
## (Optional) Install pre-commit hooks
Pre commit hooks help you lint your python code on each git commit, to avoid having to fail the build when submitting a PR. Installing pre-commit hooks is completely optional.
```cmd
pre-commit install
```
## API Endpoints
API endpoints documentation and swagger are available at [https://localhost:8000/docs](https://localhost:8000/docs)
## Project Structure
```text
management_api_app
├── api - web related stuff.
│ ├── dependencies - dependencies for routes definition.
│ ├── errors - definition of error handlers.
│ └── routes - web routes.
├── core - application configuration, startup events, logging.
├── db - db related stuff.
│ ├── migrations - manually written alembic migrations.
│ └── repositories - all crud stuff.
├── models - pydantic models for this application.
│ ├── domain - main models that are used almost everywhere.
│ └── schemas - schemas for using in web routes.
├── resources - strings that are used in web responses.
├── services - logic that is not just crud related.
└── main.py - FastAPI application creation and configuration.
```

Просмотреть файл

@ -1,17 +0,0 @@
# Developer Setup
## Install pre-commit hooks for python
Pre commit hooks help you lint your python code on each git commit, to avoid having to fail the build when submitting a PR. Installing pre-commit hooks is completely optional.
1. Install flake8, pre-commit etc.
```cmd
pip install -r requirements.txt
```
2. Enable pre-commit hooks
```cmd
pre-commit install
```

Просмотреть файл

@ -4,26 +4,28 @@ The Azure TRE uses Terraform infrastructure as code templates that pull down Doc
The most straightforward way to get up and running is to deploy direct from the `microsoft/AzureTRE` repository. Production deployments should take advantage of your chosen DevOps CD tooling.
## Pre-requisites
## Prerequisites
You will require the following prerequisites installed. They will already be present if using GitHub Codespaces, or use our Dev Container in VS Code.
You will require the following pre requisites installed. They will already be present if using GitHub Codespaces, or use our Dev Container in VS Code.
- Terraform >= v0.15.3. The following instructions use local terraform state, you may want to consider [storing you state remotely in Azure](https://docs.microsoft.com/en-us/azure/developer/terraform/store-state-in-azure-storage)
- Azure CLI >= 2.21.0
- Docker
You will also need:
- An Azure Subscription
- GitHub user id and [personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with scope `packages:read`. This token is used to pull the web app Docker images. This can be any GitHub account, and does not need to be part of the Microsoft GitHub organisation.
- A GitHub user id and [personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with scope `packages:read`. This token is used to pull the web app Docker images. This can be any GitHub account, and does not need to be part of the Microsoft GitHub organisation.
## Clone the repository
Clone the repository to your local machine ( `git clone https://github.com/microsoft/AzureTRE.git` ) or you may choose to use our pre-configured dev container via GitHub Codespaces.
Clone the repository to your local machine ( `git clone https://github.com/microsoft/AzureTRE.git` ), or you may choose to use our pre-configured dev container via GitHub Codespaces.
![Clone Options](../docs/assets/clone_options.png)
## Management Infrastructure
In the following steps we will create management infrastructure in your subscription. This includes resources, such as a storage accoutn and container registry that will enable deployment the Azure TRE. Once the infrastructure is deployed we will build the container images required for deployment.
In the following steps we will create management infrastructure in your subscription. This includes resources, such as a storage account and container registry that will enable deployment the Azure TRE. Once the infrastructure is deployed we will build the container images required for deployment.
### Log into your chosen Azure subscription
Login and select the azure subscription you wish to deploy to:
@ -49,11 +51,11 @@ Copy [/devops/terraform/.env.sample](../devops/terraform/.env.sample) to `/devop
- `TF_VAR_location` - Azure region to deploy all resources into.
- `TF_VAR_image_tag` - Default tag for docker images that will be pushed to the container registry and deployed with the Azure TRE
### Bootstrap of backend state
### Bootstrap of back-end state
As a principle we want all our resources defined in Terraform, including the storage account used by Terraform to hold backend state. This results in a chicken and egg problem.
As a principle we want all our resources defined in Terraform, including the storage account used by Terraform to hold back-end state. This results in a chicken and egg problem.
To solve this a bootstrap script is used which creates the initial storage account and resource group using the Azure CLI. Then Terraform is initialized using this storage account as a backend, and the storage account imported into state
To solve this a bootstrap script is used which creates the initial storage account and resource group using the Azure CLI. Then Terraform is initialized using this storage account as a back-end, and the storage account imported into state
- From bash run `make bootstrap`

Просмотреть файл

Просмотреть файл

@ -0,0 +1,9 @@
# Api configuration
# ------------------------
# PROJECT_NAME=""
# DEBUG=False
# State store configuration
# ------------------------
STATE_STORE_ENDPOINT=https://localhost:8081
STATE_STORE_KEY=

Просмотреть файл

@ -3,7 +3,7 @@ FROM python:3.8
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY . /api
COPY .. /api
WORKDIR /api

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,7 @@
from fastapi import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse
async def http_error_handler(_: Request, exc: HTTPException) -> JSONResponse:
return JSONResponse({"errors": [exc.detail]}, status_code=exc.status_code)

Просмотреть файл

@ -0,0 +1,25 @@
from typing import Union
from fastapi.exceptions import RequestValidationError
from fastapi.openapi.constants import REF_PREFIX
from fastapi.openapi.utils import validation_error_response_definition
from pydantic import ValidationError
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
async def http422_error_handler(_: Request, exc: Union[RequestValidationError, ValidationError]) -> JSONResponse:
return JSONResponse(
{"errors": exc.errors()},
status_code=HTTP_422_UNPROCESSABLE_ENTITY,
)
validation_error_response_definition["properties"] = {
"errors": {
"title": "Errors",
"type": "array",
"items": {"$ref": "{0}ValidationError".format(REF_PREFIX)},
},
}

Просмотреть файл

Просмотреть файл

@ -0,0 +1,8 @@
from fastapi import APIRouter
from api.routes import ping, health
router = APIRouter()
router.include_router(ping.router, tags=["ping"])
router.include_router(health.router, tags=["health"])

Просмотреть файл

@ -0,0 +1,14 @@
from fastapi import APIRouter
from models.schemas.health import HealthCheck, ServiceStatus
from resources import strings
from services.health_checker import create_state_store_status
router = APIRouter()
@router.get("/health", name="health:get-status-of-services")
async def health_check() -> HealthCheck:
status, message = create_state_store_status()
services = [ServiceStatus(service=strings.COSMOS_DB, status=status, message=message)]
return HealthCheck(services=services)

Просмотреть файл

@ -0,0 +1,12 @@
from datetime import datetime
from fastapi import APIRouter
from models.schemas.ping import Pong
from resources import strings
router = APIRouter()
@router.get("/ping", name="ping:get-server-alive")
async def ping_server() -> Pong:
return Pong(message=strings.PONG, time=datetime.now())

Просмотреть файл

Просмотреть файл

@ -0,0 +1,14 @@
from starlette.config import Config
config = Config(".env")
# api settings
API_PREFIX = "/api"
PROJECT_NAME: str = config("PROJECT_NAME", default="Azure TRE API")
DEBUG: bool = config("DEBUG", cast=bool, default=False)
VERSION = "0.0.0"
# State store configuration
STATE_STORE_ENDPOINT: str = config("STATE_STORE_ENDPOINT", default="") # cosmos db endpoint
STATE_STORE_KEY: str = config("STATE_STORE_KEY", default="") # cosmos db access key

Просмотреть файл

@ -0,0 +1,16 @@
from typing import Callable
from fastapi import FastAPI
def create_start_app_handler(app: FastAPI) -> Callable:
async def start_app() -> None:
pass
return start_app
def create_stop_app_handler(app: FastAPI) -> Callable:
async def stop_app() -> None:
pass
return stop_app

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,10 @@
version: '3'
services:
app:
build: ""
restart: on-failure
ports:
- "8000:8000"
env_file:
- .env

Просмотреть файл

@ -0,0 +1,25 @@
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException
from api.errors.http_error import http_error_handler
from api.errors.validation_error import http422_error_handler
from api.routes.api import router as api_router
from core.config import API_PREFIX, DEBUG, PROJECT_NAME, VERSION
from core.events import create_start_app_handler, create_stop_app_handler
def get_application() -> FastAPI:
application = FastAPI(title=PROJECT_NAME, debug=DEBUG, version=VERSION)
application.add_event_handler("startup", create_start_app_handler(application))
application.add_event_handler("shutdown", create_stop_app_handler(application))
application.add_exception_handler(HTTPException, http_error_handler)
application.add_exception_handler(RequestValidationError, http422_error_handler)
application.include_router(api_router, prefix=API_PREFIX)
return application
app = get_application()

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,19 @@
from enum import Enum
from typing import List
from pydantic import BaseModel
from resources import strings
class StatusEnum(str, Enum):
ok = strings.OK
not_ok = strings.NOT_OK
class ServiceStatus(BaseModel):
service: str = ""
status: StatusEnum = StatusEnum.ok
message: str = ""
class HealthCheck(BaseModel):
services: List[ServiceStatus]

Просмотреть файл

@ -0,0 +1,7 @@
from datetime import datetime
from pydantic import BaseModel
class Pong(BaseModel):
message: str
time: datetime

Просмотреть файл

@ -0,0 +1,7 @@
# api
Click==7.1.2
fastapi[all]==0.63.0
uvicorn[standard]==0.13.4
gunicorn==20.1.0
azure-core==1.13.0
azure-cosmos==4.2.0

Просмотреть файл

Просмотреть файл

@ -0,0 +1,7 @@
OK = "OK"
NOT_OK = "Not OK"
PONG = "pong"
COSMOS_DB = "Cosmos DB"
STATE_STORE_ENDPOINT_NOT_RESPONDING = "Server is not responding"
UNSPECIFIED_ERROR = "Unspecified error"

Просмотреть файл

Просмотреть файл

@ -0,0 +1,19 @@
from azure.core import exceptions
from azure.cosmos import CosmosClient
from core.config import STATE_STORE_ENDPOINT, STATE_STORE_KEY
from models.schemas.health import StatusEnum
from resources import strings
def create_state_store_status() -> (StatusEnum, str):
status = StatusEnum.ok
message = ""
try:
client = CosmosClient(STATE_STORE_ENDPOINT, STATE_STORE_KEY) # noqa: F841 - flake 8 client is not used
except exceptions.ServiceRequestError:
status = StatusEnum.not_ok
message = strings.STATE_STORE_ENDPOINT_NOT_RESPONDING
except: # noqa: E722 flake8 - no bare excepts
status = StatusEnum.not_ok
message = strings.UNSPECIFIED_ERROR
return status, message

Просмотреть файл

Просмотреть файл

@ -0,0 +1,6 @@
# import os
# import sys
# TEST_DIR = os.path.dirname(os.path.abspath(__file__))
# PROJECT_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir, 'api'))
# sys.path.insert(0, PROJECT_DIR)

Просмотреть файл

@ -0,0 +1,22 @@
import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from asgi_lifespan import LifespanManager
@pytest.fixture
def app() -> FastAPI:
from main import get_application
return get_application()
@pytest.fixture
async def initialized_app(app: FastAPI) -> FastAPI:
async with LifespanManager(app):
yield app
@pytest.fixture
async def client(initialized_app: FastAPI) -> AsyncClient:
async with AsyncClient(app=initialized_app, base_url="http://testserver", headers={"Content-Type": "application/json"}) as client:
yield client

Просмотреть файл

Просмотреть файл

@ -0,0 +1,20 @@
import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
pytestmark = pytest.mark.asyncio
async def test_frw_validation_error_format(app: FastAPI):
@app.get("/wrong_path/{param}")
def route_for_test(param: int) -> None: # pragma: no cover
pass
async with AsyncClient(base_url="http://testserver", app=app) as client:
response = await client.get("/wrong_path/asd")
assert response.status_code == HTTP_422_UNPROCESSABLE_ENTITY
assert "errors" in response.json()

Просмотреть файл

@ -0,0 +1,15 @@
import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from starlette.status import HTTP_404_NOT_FOUND
pytestmark = pytest.mark.asyncio
async def test_frw_validation_error_format(app: FastAPI):
async with AsyncClient(base_url="http://testserver", app=app) as client:
response = await client.get("/wrong_path/asd")
assert response.status_code == HTTP_404_NOT_FOUND
assert "errors" in response.json()

Просмотреть файл

Просмотреть файл

@ -0,0 +1,19 @@
import pytest
from mock import patch
from fastapi import FastAPI
from httpx import AsyncClient
from resources import strings
from models.schemas.health import StatusEnum
pytestmark = pytest.mark.asyncio
@patch("api.routes.health.create_state_store_status")
async def test_health_response_contains_cosmos_status(health_check_mock, app: FastAPI, client: AsyncClient) -> None:
message = ""
health_check_mock.return_value = StatusEnum.ok, message
response = await client.get(app.url_path_for("health:get-status-of-services"))
assert {"message": message, "service": strings.COSMOS_DB, "status": strings.OK} in response.json()["services"]

Просмотреть файл

@ -0,0 +1,12 @@
import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from resources import strings
pytestmark = pytest.mark.asyncio
async def test_ping(app: FastAPI, client: AsyncClient) -> None:
response = await client.get(app.url_path_for("ping:get-server-alive"))
assert response.json()["message"] == strings.PONG

Просмотреть файл

Просмотреть файл

@ -0,0 +1,38 @@
from mock import patch
from azure.core.exceptions import ServiceRequestError
from models.schemas.health import StatusEnum
from resources import strings
from services import health_checker
@patch("services.health_checker.CosmosClient")
def test_get_state_store_status_responding(cosmos_client_mock) -> None:
cosmos_client_mock.return_value = None
status, message = health_checker.create_state_store_status()
assert status == StatusEnum.ok
assert message == ""
@patch("services.health_checker.CosmosClient")
def test_get_state_store_status_not_responding(cosmos_client_mock) -> None:
cosmos_client_mock.return_value = None
cosmos_client_mock.side_effect = ServiceRequestError(message="some message")
status, message = health_checker.create_state_store_status()
assert status == StatusEnum.not_ok
assert message == strings.STATE_STORE_ENDPOINT_NOT_RESPONDING
@patch("services.health_checker.CosmosClient")
def test_get_state_store_status_other_exception(cosmos_client_mock) -> None:
cosmos_client_mock.return_value = None
cosmos_client_mock.side_effect = Exception()
status, message = health_checker.create_state_store_status()
assert status == StatusEnum.not_ok
assert message == strings.UNSPECIFIED_ERROR

Просмотреть файл

@ -1,7 +1,16 @@
# requirements for api
Click==7.1.2
fastapi[all]==0.63.0
fastapi[all]==0.65.1
uvicorn[standard]==0.13.4
gunicorn==20.1.0
azure-core==1.13.0
azure-cosmos==4.2.0
# dev requirements
pytest==6.2.4
pytest-asyncio~=0.14.0
flake8==3.9.1
httpx~=0.18.1
asgi-lifespan~=1.0.1
pre-commit==2.12.1
mock==4.0.3

Просмотреть файл

@ -58,6 +58,17 @@ resource "azurerm_application_gateway" "agw" {
protocol = "Http"
}
probe {
name = "management-api"
pick_host_name_from_backend_http_settings = true
interval = 10
protocol = "Https"
path = "/api/health"
timeout = 10
unhealthy_threshold = 2
minimum_servers = 0
}
request_routing_rule {
name = local.request_routing_rule_name
rule_type = "PathBasedRouting"
@ -72,7 +83,7 @@ resource "azurerm_application_gateway" "agw" {
path_rule {
name = "api"
paths = ["/*"]
paths = ["/api/*"]
backend_address_pool_name = local.management_api_backend_address_pool_name
backend_http_settings_name = local.http_setting_name
}