Skip to content

Commit c86769c

Browse files
authored
Merge pull request #41 from localstack/user/alexlave100/nosqlapi-sample
[Azure:CosmosDB] - NoSQL API vacation planner sample
2 parents d1df065 + dc599f7 commit c86769c

61 files changed

Lines changed: 61460 additions & 6 deletions

Some content is hidden

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

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ This repository contains comprehensive sample projects demonstrating how to deve
2626

2727
| Sample Name | Description |
2828
|-------------|-------------|
29-
| [Function App and Storage](./samples/function-app-storage-http/dotnet) | Azure Functions App using Blob, Queue, and Table Storage |
30-
| [Function App and Front Door](./samples/function-app-front-door/python/) | Azure Functions App exposed via Front Door |
31-
| [Function App and Managed Identities](./samples/function-app-managed-identity/python/) | Azure Function App using Managed Identities |
32-
| [Web App and CosmosDB ](./samples/web-app-cosmosdb-mongodb-api/python/) | Azure Web App using CosmosDB for MongoDB API |
33-
| [Web App and Managed Identities](./samples/web-app-managed-identity/python/) | Azure Web App using Managed Identities |
34-
| [Web App and SQL Database ](./samples/web-app-sql-database/python/) | Azure Web App using SQL Database |
29+
| [Function App and Storage](./samples/function-app-storage-http/dotnet/README.md) | Azure Functions App using Blob, Queue, and Table Storage |
30+
| [Function App and Front Door](./samples/function-app-front-door/python/README.md) | Azure Functions App exposed via Front Door |
31+
| [Function App and Managed Identities](./samples/function-app-managed-identity/python/README.md) | Azure Function App using Managed Identities |
32+
| [Web App and CosmosDB for MongoDB API ](./samples/web-app-cosmosdb-mongodb-api/python/README.md) | Azure Web App using CosmosDB for MongoDB API |
33+
| [Web App and CosmosDB for NoSQL API ](./samples/web-app-cosmosdb-nosql-api/python/README.md) | Azure Web App using CosmosDB for NoSQL API |
34+
| [Web App and Managed Identities](./samples/web-app-managed-identity/python/README.md) | Azure Web App using Managed Identities |
35+
| [Web App and SQL Database ](./samples/web-app-sql-database/python/README.md) | Azure Web App using SQL Database |
3536

3637
## Sample Structure
3738

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
# Azure Web App with Azure CosmosDB for NoSQL API
2+
3+
This sample demonstrates a Python Flask single-page web application called *Vacation Planner* hosted on an [Azure Web App](https://learn.microsoft.com/en-us/azure/app-service/overview). The app runs on an Azure App Service Plan and stores activity data in the `activities` container of the `sampledb` NoSQL database on an [Azure CosmosDB for NoSQL](https://learn.microsoft.com/en-us/azure/cosmos-db/distributed-nosql) account.
4+
5+
## Architecture
6+
7+
The following diagram illustrates the architecture of the solution:
8+
9+
![Architecture Diagram](./images/architecture.png)
10+
11+
- **Azure Web App**: Hosts the Python Flask application
12+
- **Azure App Service Plan**: Provides compute resources for the web app
13+
- **Azure CosmosDB for NoSQL API**: Stores activity data in a CosmosDB container
14+
15+
## Prerequisites
16+
17+
- [Azure Subscription](https://azure.microsoft.com/free/)
18+
- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)
19+
- [Python 3.11+](https://www.python.org/downloads/)
20+
- [Flask](https://flask.palletsprojects.com/)
21+
22+
## Deployment
23+
24+
Set up the Azure emulator using the LocalStack for Azure Docker image. Before starting, ensure you have a valid `LOCALSTACK_AUTH_TOKEN` to access the Azure emulator. Refer to the [Auth Token guide](https://docs.localstack.cloud/getting-started/auth-token/?__hstc=108988063.8aad2b1a7229945859f4d9b9bb71e05d.1743148429561.1758793541854.1758810151462.32&__hssc=108988063.3.1758810151462&__hsfp=3945774529) to obtain your Auth Token and set it in the `LOCALSTACK_AUTH_TOKEN` environment variable. The Azure Docker image is available on the [LocalStack Docker Hub](https://hub.docker.com/r/localstack/localstack-azure-alpha). To pull the image, execute:
25+
26+
```bash
27+
docker pull localstack/localstack-azure-alpha
28+
```
29+
30+
Start the LocalStack Azure emulator by running:
31+
32+
```bash
33+
export LOCALSTACK_AUTH_TOKEN=<your_auth_token>
34+
IMAGE_NAME=localstack/localstack-azure-alpha localstack start
35+
```
36+
37+
Deploy the application to LocalStack for Azure using:
38+
39+
- [Azure CLI Deployment](./scripts/README.md)
40+
41+
> **Note**
42+
> When you deploy the application to LocalStack for Azure for the first time, the initialization process involves downloading and building Docker images. This is a one-time operation—subsequent deployments will be significantly faster. Depending on your internet connection and system resources, this initial setup may take several minutes.
43+
44+
## Test
45+
46+
1. Retrieve the port published and mapped to port 80 by the Docker container hosting the emulated Web App.
47+
2. Open a web browser and navigate to `http://localhost:<published-port>`.
48+
3. If the deployment was successful, you will see the following user interface for adding and removing activities:
49+
50+
![Architecture Diagram](./images/vacation-planner.png)
51+
52+
You can use the `call-web-app.sh` Bash script below to call the web app. The script demonstrates three methods for calling web apps:
53+
54+
1. **Through the LocalStack for Azure emulator**: Call the web app via the emulator using its default host name. The emulator acts as a proxy to the web app.
55+
2. **Via localhost and host port mapped to the container's port**: Use `127.0.0.1` with the host port mapped to the container's port `80`.
56+
3. **Via container IP address**: Use the app container's IP address on port `80`. This technique is only available when accessing the web app from the Docker host machine.
57+
4. **Via Runtime Gateway**: Use the `{web_app_name}website.localhost.localstack.cloud:4566` URL to call the web app via the LocalStack runtime gateway.
58+
59+
```bash
60+
#!/bin/bash
61+
62+
get_docker_container_name_by_prefix() {
63+
local app_prefix="$1"
64+
local container_name
65+
66+
# Check if Docker is running
67+
if ! docker info >/dev/null 2>&1; then
68+
echo "Error: Docker is not running" >&2
69+
return 1
70+
fi
71+
72+
echo "Looking for containers with names starting with [$app_prefix]..." >&2
73+
74+
# Find the container using grep
75+
container_name=$(docker ps --format "{{.Names}}" | grep "^${app_prefix}" | head -1)
76+
77+
if [ -z "$container_name" ]; then
78+
echo "Error: No running container found with name starting with [$app_prefix]" >&2
79+
return 1
80+
fi
81+
82+
echo "Found matching container [$container_name]" >&2
83+
echo "$container_name"
84+
}
85+
86+
get_docker_container_ip_address_by_name() {
87+
local container_name="$1"
88+
local ip_address
89+
90+
if [ -z "$container_name" ]; then
91+
echo "Error: Container name is required" >&2
92+
return 1
93+
fi
94+
95+
# Get IP address
96+
ip_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container_name")
97+
98+
if [ -z "$ip_address" ]; then
99+
echo "Error: Container [$container_name] has no IP address assigned" >&2
100+
return 1
101+
fi
102+
103+
echo "$ip_address"
104+
}
105+
106+
get_docker_container_port_mapping() {
107+
local container_name="$1"
108+
local container_port="$2"
109+
local host_port
110+
111+
if [ -z "$container_name" ] || [ -z "$container_port" ]; then
112+
echo "Error: Container name and container port are required" >&2
113+
return 1
114+
fi
115+
116+
# Get host port mapping
117+
host_port=$(docker inspect -f "{{(index (index .NetworkSettings.Ports \"${container_port}/tcp\") 0).HostPort}}" "$container_name")
118+
119+
if [ -z "$host_port" ]; then
120+
echo "Error: No host port mapping found for container [$container_name] port [$container_port]" >&2
121+
return 1
122+
fi
123+
124+
echo "$host_port"
125+
}
126+
127+
call_web_app() {
128+
# Get the web app name
129+
echo "Getting web app name..."
130+
web_app_name=$(azlocal webapp list --query '[0].name' --output tsv)
131+
132+
if [ -n "$web_app_name" ]; then
133+
echo "Web app [$web_app_name] successfully retrieved."
134+
else
135+
echo "Error: No web app found"
136+
exit 1
137+
fi
138+
139+
# Get the resource group name
140+
echo "Getting resource group name for web app [$web_app_name]..."
141+
resource_group_name=$(azlocal webapp list --query '[0].resourceGroup' --output tsv)
142+
143+
if [ -n "$resource_group_name" ]; then
144+
echo "Resource group [$resource_group_name] successfully retrieved."
145+
else
146+
echo "Error: No resource group found for web app [$web_app_name]"
147+
exit 1
148+
fi
149+
150+
# Get the the default host name of the web app
151+
echo "Getting the default host name of the web app [$web_app_name]..."
152+
app_host_name=$(azlocal webapp show \
153+
--name "$web_app_name" \
154+
--resource-group "$resource_group_name" \
155+
--query 'defaultHostName' \
156+
--output tsv)
157+
158+
if [ -n "$app_host_name" ]; then
159+
echo "Web app default host name [$app_host_name] successfully retrieved."
160+
else
161+
echo "Error: No web app default host name found"
162+
exit 1
163+
fi
164+
165+
# Get the Docker container name
166+
echo "Finding container name with prefix [ls-$web_app_name]..."
167+
container_name=$(get_docker_container_name_by_prefix "ls-$web_app_name")
168+
169+
if [ $? -eq 0 ] && [ -n "$container_name" ]; then
170+
echo "Container [$container_name] found successfully"
171+
else
172+
echo "Failed to get container name"
173+
exit 1
174+
fi
175+
176+
# Get the container IP address
177+
echo "Getting IP address for container [$container_name]..."
178+
container_ip=$(get_docker_container_ip_address_by_name "$container_name")
179+
180+
if [ $? -eq 0 ] && [ -n "$container_ip" ]; then
181+
echo "IP address [$container_ip] retrieved successfully for container [$container_name]"
182+
else
183+
echo "Failed to get container IP address"
184+
exit 1
185+
fi
186+
187+
# Get the mapped host port for web app HTTP trigger (internal port 80)
188+
echo "Getting the host port mapped to internal port 80 in container [$container_name]..."
189+
host_port=$(get_docker_container_port_mapping "$container_name" "80")
190+
191+
if [ $? -eq 0 ] && [ -n "$host_port" ]; then
192+
echo "Mapped host port [$host_port] retrieved successfully for container [$container_name]"
193+
else
194+
echo "Failed to get mapped host port for container [$container_name]"
195+
exit 1
196+
fi
197+
198+
# Retrieve LocalStack proxy port
199+
proxy_port=$(curl http://localhost:4566/_localstack/proxy -s | jq '.proxy_port')
200+
201+
if [ -n "$proxy_port" ]; then
202+
# Call the web app via emulator proxy
203+
echo "Calling web app [$web_app_name] via emulator..."
204+
curl --proxy "http://localhost:$proxy_port/" -s "http://$app_host_name/" 1> /dev/null
205+
206+
if [ $? == 0 ]; then
207+
echo "Web app call via emulator proxy port [$proxy_port] succeeded."
208+
else
209+
echo "Web app call via emulator proxy port [$proxy_port] failed."
210+
fi
211+
else
212+
echo "Failed to retrieve LocalStack proxy port"
213+
fi
214+
215+
if [ -n "$container_ip" ]; then
216+
# Call the web app via the container IP address
217+
echo "Calling web app [$web_app_name] via container IP address [$container_ip]..."
218+
curl -s "http://$container_ip/" 1> /dev/null
219+
220+
if [ $? == 0 ]; then
221+
echo "Web app call via container IP address [$container_ip] succeeded."
222+
else
223+
echo "Web app call via container IP address [$container_ip] failed."
224+
fi
225+
else
226+
echo "Failed to retrieve container IP address"
227+
fi
228+
229+
if [ -n "$host_port" ]; then
230+
# Call the web app via the host port
231+
echo "Calling web app [$web_app_name] via host port [$host_port]..."
232+
curl -s "http://127.0.0.1:$host_port/" 1> /dev/null
233+
234+
if [ $? == 0 ]; then
235+
echo "Web app call via host port [$host_port] succeeded."
236+
else
237+
echo "Web app call via host port [$host_port] failed."
238+
fi
239+
else
240+
echo "Failed to retrieve host port"
241+
fi
242+
243+
gateway_port=4566
244+
245+
if [ -n "$gateway_port" ]; then
246+
# Call the web app via the runtime gateway
247+
echo "Calling web app [$web_app_name] via runtime gateway on port [$gateway_port]..."
248+
curl -s "http://${web_app_name}website.localhost.localstack.cloud:$gateway_port/" 1> /dev/null
249+
250+
if [ $? == 0 ]; then
251+
echo "Web app call via runtime gateway on port [$gateway_port] succeeded."
252+
else
253+
echo "Web app call via runtime gateway on port [$gateway_port] failed."
254+
fi
255+
else
256+
echo "Failed to retrieve runtime gateway port"
257+
fi
258+
}
259+
260+
call_web_app
261+
```
262+
263+
## CosmosDB Tooling
264+
265+
You can utilize **CosmosDB Data Explorer** to explore and manage your CosmosDB databases and containers. Ensure you connect using `http://localhost:port` connection string, where `port` corresponds to the port published by the CosmosDB container on the host and mapped to the internal CosmosDB port `1234`.
266+
267+
![CosmosDB Data Explorer](./images/nosql-api-data-explorer.png)
268+
269+
## References
270+
271+
- [Azure Web Apps Documentation](https://learn.microsoft.com/en-us/azure/app-service/)
272+
- [Azure CosmosDB Documentation](https://learn.microsoft.com/en-us/azure/cosmos-db/)
273+
- [Quickstart: Python Flask on Azure](https://learn.microsoft.com/en-us/azure/app-service/quickstart-python?tabs=flask%2Cbrowser)
274+
- [Azure Identity Client Library for Python](https://learn.microsoft.com/en-us/python/api/overview/azure/identity-readme?view=azure-python)
275+
- [LocalStack for Azure](https://azure.localstack.cloud/)
57.1 KB
Loading
96.7 KB
Loading
211 KB
Loading

0 commit comments

Comments
 (0)