Skip to content

Commit ee9caa4

Browse files
committed
initial commit
1 parent 1f1ea6f commit ee9caa4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+10566
-24
lines changed

README.md

Lines changed: 101 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,110 @@
1-
# Project
1+
This repository includes a simple Python Flask API with a single route that returns JSON.
2+
You can use this project as a starting point for your own APIs.
23

3-
> This repo has been populated by an initial template to help get you started. Please
4-
> make sure to update the content to build a great experience for community-building.
4+
The repository is designed for use with [Docker containers](https://www.docker.com/), both for local development and deployment, and includes infrastructure files for deployment to [Azure Container Apps](https://learn.microsoft.com/azure/container-apps/overview). 🐳
55

6-
As the maintainer of this project, please make a few updates:
6+
The code is organized using [Flask Blueprints](https://flask.palletsprojects.com/en/2.2.x/blueprints/),
7+
tested with [pytest](https://docs.pytest.org/en/7.2.x/),
8+
linted with [ruff](https://github.com/charliermarsh/ruff), and formatted with [black](https://black.readthedocs.io/en/stable/).
9+
Code quality issues are all checked with both [pre-commit](https://pre-commit.com/) and Github actions.
710

8-
- Improving this README.MD file to provide a great experience
9-
- Updating SUPPORT.MD with content about this project's support experience
10-
- Understanding the security reporting process in SECURITY.MD
11-
- Remove this section from the README
11+
## Opening the project
1212

13-
## Contributing
13+
This project has [Dev Container support](https://code.visualstudio.com/docs/devcontainers/containers), so it will be be setup automatically if you open it in Github Codespaces or in local VS Code with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
1414

15-
This project welcomes contributions and suggestions. Most contributions require you to agree to a
16-
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
17-
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
15+
If you're not using one of those options for opening the project, then you'll need to:
1816

19-
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
20-
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
21-
provided by the bot. You will only need to do this once across all repos using our CLA.
17+
1. Create a [Python virtual environment](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments) and activate it.
2218

23-
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
24-
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
25-
contact [[email protected]](mailto:[email protected]) with any additional questions or comments.
19+
2. Install the requirements:
2620

27-
## Trademarks
21+
```shell
22+
python3 -m pip install -r src/requirements-dev.txt
23+
```
2824

29-
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
30-
trademarks or logos is subject to and must follow
31-
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
32-
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
33-
Any use of third-party trademarks or logos are subject to those third-party's policies.
25+
3. Install the pre-commit hooks:
26+
27+
```shell
28+
pre-commit install
29+
```
30+
31+
## Local development
32+
33+
1. Run the local server:
34+
35+
```shell
36+
python3 -m flask --debug --app src/app:app run --port 5000
37+
```
38+
39+
3. Click 'http://127.0.0.1:5000' in the terminal, which should open a new tab in the browser.
40+
41+
4. Try the API at '/generate_name' and try passing in a parameter at the end of the URL, like '/generate_name?start_with=N'.
42+
43+
### Local development with Docker
44+
45+
You can also run this app with Docker, thanks to the `Dockerfile`.
46+
You need to either have Docker Desktop installed or have this open in Github Codespaces for these commands to work.
47+
48+
1. Build the image:
49+
50+
```
51+
docker build --tag flask-app src/
52+
```
53+
54+
2. Run the image:
55+
56+
```
57+
docker run --publish 5000:5000 flask-app
58+
```
59+
60+
### Deployment
61+
62+
This repo is set up for deployment on Azure Container Apps using the configuration files in the `infra` folder.
63+
64+
This diagram shows the architecture of the deployment:
65+
66+
![Diagram of app architecture: Azure Container Apps environment, Azure Container App, Azure Container Registry, Container, and Key Vault](readme_diagram.png)
67+
68+
Steps for deployment:
69+
70+
1. Sign up for a [free Azure account](https://azure.microsoft.com/free/) and create an Azure Subscription.
71+
2. Install the [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd). (If you open this repository in Codespaces or with the VS Code Dev Containers extension, that part will be done for you.)
72+
3. Login to Azure:
73+
74+
```shell
75+
azd auth login
76+
```
77+
78+
4. Provision and deploy all the resources:
79+
80+
```shell
81+
azd up
82+
```
83+
It will prompt you to provide an `azd` environment name (like "flask-app"), select a subscription from your Azure account, and select a location (like "eastus"). Then it will provision the resources in your account and deploy the latest code. If you get an error with deployment, changing the location can help, as there may be availability constraints for some of the resources.
84+
85+
5. When `azd` has finished deploying, you'll see an endpoint URI in the command output. Visit that URI, and you should see the API output! 🎉
86+
6. When you've made any changes to the app code, you can just run:
87+
88+
```shell
89+
azd deploy
90+
```
91+
92+
### Costs
93+
94+
Pricing varies per region and usage, so it isn't possible to predict exact costs for your usage.
95+
The majority of the Azure resources used in this infrastructure are on usage-based pricing tiers.
96+
However, Azure Container Registry has a fixed cost per registry per day.
97+
98+
You can try the [Azure pricing calculator](https://azure.com/e/9f8185b239d240b398e201078d0c4e7a) for the resources:
99+
100+
- Azure Container App: Consumption tier with 0.5 CPU, 1GiB memory/storage. Pricing is based on resource allocation, and each month allows for a certain amount of free usage. [Pricing](https://azure.microsoft.com/pricing/details/container-apps/)
101+
- Azure Container Registry: Basic tier. [Pricing](https://azure.microsoft.com/pricing/details/container-registry/)
102+
- Log analytics: Pay-as-you-go tier. Costs based on data ingested. [Pricing](https://azure.microsoft.com/pricing/details/monitor/)
103+
104+
⚠️ To avoid unnecessary costs, remember to take down your app if it's no longer in use,
105+
either by deleting the resource group in the Portal or running `azd down`.
106+
107+
108+
## Getting help
109+
110+
If you're working with this project and running into issues, please post in **Discussions**.

azure.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
3+
name: simple-flask-container-app
4+
metadata:
5+
6+
services:
7+
api:
8+
project: ./src
9+
language: py
10+
host: containerapp

infra/api.bicep

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
param name string
2+
param location string = resourceGroup().location
3+
param tags object = {}
4+
5+
param identityName string
6+
param containerAppsEnvironmentName string
7+
param containerRegistryName string
8+
param serviceName string = 'api'
9+
param exists bool
10+
11+
resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
12+
name: identityName
13+
location: location
14+
}
15+
16+
module app 'core/host/container-app-upsert.bicep' = {
17+
name: '${serviceName}-container-app-module'
18+
params: {
19+
name: name
20+
location: location
21+
tags: union(tags, { 'azd-service-name': serviceName })
22+
identityName: apiIdentity.name
23+
exists: exists
24+
containerAppsEnvironmentName: containerAppsEnvironmentName
25+
containerRegistryName: containerRegistryName
26+
targetPort: 5000
27+
}
28+
}
29+
30+
output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId
31+
output SERVICE_API_NAME string = app.outputs.name
32+
output SERVICE_API_URI string = app.outputs.uri
33+
output SERVICE_API_IMAGE_NAME string = app.outputs.imageName
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
param name string
2+
param location string = resourceGroup().location
3+
param tags object = {}
4+
@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.')
5+
param customSubDomainName string = name
6+
param deployments array = []
7+
param kind string = 'OpenAI'
8+
param publicNetworkAccess string = 'Enabled'
9+
param sku object = {
10+
name: 'S0'
11+
}
12+
13+
resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = {
14+
name: name
15+
location: location
16+
tags: tags
17+
kind: kind
18+
properties: {
19+
customSubDomainName: customSubDomainName
20+
publicNetworkAccess: publicNetworkAccess
21+
}
22+
sku: sku
23+
}
24+
25+
@batchSize(1)
26+
resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2022-10-01' = [for deployment in deployments: {
27+
parent: account
28+
name: deployment.name
29+
properties: {
30+
model: deployment.model
31+
raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null
32+
scaleSettings: deployment.scaleSettings
33+
}
34+
}]
35+
36+
output endpoint string = account.properties.endpoint
37+
output id string = account.id
38+
output name string = account.name
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
param name string
2+
param location string = resourceGroup().location
3+
param tags object = {}
4+
5+
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
6+
param keyVaultName string
7+
8+
@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ])
9+
param kind string
10+
11+
resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = {
12+
name: name
13+
kind: kind
14+
location: location
15+
tags: tags
16+
properties: {
17+
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
18+
locations: [
19+
{
20+
locationName: location
21+
failoverPriority: 0
22+
isZoneRedundant: false
23+
}
24+
]
25+
databaseAccountOfferType: 'Standard'
26+
enableAutomaticFailover: false
27+
enableMultipleWriteLocations: false
28+
apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.0' } : {}
29+
capabilities: [ { name: 'EnableServerless' } ]
30+
}
31+
}
32+
33+
resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
34+
parent: keyVault
35+
name: connectionStringKey
36+
properties: {
37+
value: cosmos.listConnectionStrings().connectionStrings[0].connectionString
38+
}
39+
}
40+
41+
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
42+
name: keyVaultName
43+
}
44+
45+
output connectionStringKey string = connectionStringKey
46+
output endpoint string = cosmos.properties.documentEndpoint
47+
output id string = cosmos.id
48+
output name string = cosmos.name
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
param name string
2+
param location string = resourceGroup().location
3+
param tags object = {}
4+
5+
param keyVaultName string
6+
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
7+
8+
module cosmos '../../cosmos/cosmos-account.bicep' = {
9+
name: 'cosmos-account'
10+
params: {
11+
name: name
12+
location: location
13+
connectionStringKey: connectionStringKey
14+
keyVaultName: keyVaultName
15+
kind: 'MongoDB'
16+
tags: tags
17+
}
18+
}
19+
20+
output connectionStringKey string = cosmos.outputs.connectionStringKey
21+
output endpoint string = cosmos.outputs.endpoint
22+
output id string = cosmos.outputs.id
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
param accountName string
2+
param databaseName string
3+
param location string = resourceGroup().location
4+
param tags object = {}
5+
6+
param collections array = []
7+
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
8+
param keyVaultName string
9+
10+
module cosmos 'cosmos-mongo-account.bicep' = {
11+
name: 'cosmos-mongo-account'
12+
params: {
13+
name: accountName
14+
location: location
15+
keyVaultName: keyVaultName
16+
tags: tags
17+
connectionStringKey: connectionStringKey
18+
}
19+
}
20+
21+
resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = {
22+
name: '${accountName}/${databaseName}'
23+
tags: tags
24+
properties: {
25+
resource: { id: databaseName }
26+
}
27+
28+
resource list 'collections' = [for collection in collections: {
29+
name: collection.name
30+
properties: {
31+
resource: {
32+
id: collection.id
33+
shardKey: { _id: collection.shardKey }
34+
indexes: [ { key: { keys: [ collection.indexKey ] } } ]
35+
}
36+
}
37+
}]
38+
39+
dependsOn: [
40+
cosmos
41+
]
42+
}
43+
44+
output connectionStringKey string = connectionStringKey
45+
output databaseName string = databaseName
46+
output endpoint string = cosmos.outputs.endpoint
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
param name string
2+
param location string = resourceGroup().location
3+
param tags object = {}
4+
5+
param keyVaultName string
6+
7+
module cosmos '../../cosmos/cosmos-account.bicep' = {
8+
name: 'cosmos-account'
9+
params: {
10+
name: name
11+
location: location
12+
tags: tags
13+
keyVaultName: keyVaultName
14+
kind: 'GlobalDocumentDB'
15+
}
16+
}
17+
18+
output connectionStringKey string = cosmos.outputs.connectionStringKey
19+
output endpoint string = cosmos.outputs.endpoint
20+
output id string = cosmos.outputs.id
21+
output name string = cosmos.outputs.name

0 commit comments

Comments
 (0)