Skip to content

Commit

Permalink
docs: add fastapi tutorial spread test
Browse files Browse the repository at this point in the history
  • Loading branch information
erinecon committed Feb 6, 2025
1 parent 89b52c8 commit b1ddb79
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/tutorial/code/fastapi/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}
9 changes: 9 additions & 0 deletions docs/tutorial/code/fastapi/greeting_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import os

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": os.getenv("APP_GREETING", "Hello World")}
9 changes: 9 additions & 0 deletions docs/tutorial/code/fastapi/greeting_charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# configuration snippet for FastAPI application with a configuration

config:
options:
greeting:
description: |
The greeting to be returned by the FastAPI application.
default: "Hello, world!"
type: string
2 changes: 2 additions & 0 deletions docs/tutorial/code/fastapi/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fastapi[standard]
psycopg2-binary
206 changes: 206 additions & 0 deletions docs/tutorial/code/fastapi/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
###########################################
# IMPORTANT
# Comments matter!
# The docs use the wrapping comments as
# markers for including said instructions
# as snippets in the docs.
###########################################
summary: Getting started with FastAPI tutorial

kill-timeout: 180m

environment:

execute: |
# Move everything to $HOME so that Juju deployment works
mv *.yaml *.py *.txt $HOME
cd $HOME
# Don't use the staging store for this test
unset CHARMCRAFT_STORE_API_URL
unset CHARMCRAFT_UPLOAD_URL
unset CHARMCRAFT_REGISTRY_URL
# MicroK8s config setup
microk8s status --wait-ready
microk8s enable hostpath-storage
microk8s enable registry
microk8s enable ingress
# Bootstrap controller
juju bootstrap microk8s dev-controller
# [docs:create-venv]
sudo apt-get update && sudo apt-get install python3-venv -y
python3 -m venv .venv
source .venv/bin/activate
# [docs:create-venv-end]
# [docs:install-requirements]
pip install -r requirements.txt
# [docs:install-requirements-end]
fastapi dev app.py --port 8080 &
retry -n 5 --wait 2 curl --fail localhost:8080
# [docs:curl-fastapi]
curl localhost:8080
# [docs:curl-fastapi-end]
kill $!
# [docs:create-rockcraft-yaml]
rockcraft init --profile fastapi-framework
# [docs:create-rockcraft-yaml-end]
sed -i "s/name: .*/name: fastapi-hello-world/g" rockcraft.yaml
sed -i "s/amd64/$(dpkg --print-architecture)/g" rockcraft.yaml
# [docs:pack]
ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack
# [docs:pack-end]
# [docs:skopeo-copy]
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
oci-archive:fastapi-hello-world_0.1_$(dpkg --print-architecture).rock \
docker://localhost:32000/fastapi-hello-world:0.1
# [docs:skopeo-copy-end]
# [docs:create-charm-dir]
mkdir charm
cd charm
# [docs:create-charm-dir-end]
# [docs:charm-init]
charmcraft init --profile fastapi-framework --name fastapi-hello-world
# [docs:charm-init-end]
# update platforms in charmcraft.yaml file
sed -i "s/amd64/$(dpkg --print-architecture)/g" charmcraft.yaml
# [docs:charm-pack]
CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack
# [docs:charm-pack-end]
# [docs:add-juju-model]
juju add-model fastapi-hello-world
# [docs:add-juju-model-end]
#[docs:add-model-constraints]
juju set-model-constraints -m fastapi-hello-world arch=$(dpkg --print-architecture)
#[docs:add-model-constraints-end]
# [docs:deploy-fastapi-app]
juju deploy \
./fastapi-hello-world_$(dpkg --print-architecture).charm \
fastapi-hello-world --resource \
app-image=localhost:32000/fastapi-hello-world:0.1
# [docs:deploy-fastapi-app-end]
# [docs:deploy-nginx]
juju deploy nginx-ingress-integrator --channel=latest/stable --trust
juju integrate nginx-ingress-integrator fastapi-hello-world
# [docs:deploy-nginx-end]
# [docs:config-nginx]
juju config nginx-ingress-integrator \
service-hostname=fastapi-hello-world path-routes=/
# [docs:config-nginx-end]
# give Juju some time to deploy the apps
juju wait-for application fastapi-hello-world --query='status=="active"' --timeout 10m
juju wait-for application nginx-ingress-integrator --query='status=="active"' --timeout 10m
# [docs:curl-init-deployment]
curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1
# [docs:curl-init-deployment-end]
cd ..
cat greeting_app.py > app.py
sed -i "s/version: .*/version: 0.2/g" rockcraft.yaml
# [docs:docker-update]
ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
oci-archive:fastapi-hello-world_0.2_$(dpkg --print-architecture).rock \
docker://localhost:32000/fastapi-hello-world:0.2
# [docs:docker-update-end]
cat greeting_charmcraft.yaml >> ./charm/charmcraft.yaml
cd charm
# [docs:refresh-deployment]
CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack
juju refresh fastapi-hello-world \
--path=./fastapi-hello-world_$(dpkg --print-architecture).charm \
--resource app-image=localhost:32000/fastapi-hello-world:0.2
# [docs:refresh-deployment-end]
# give Juju some time to refresh the app
juju wait-for application fastapi-hello-world --query='status=="active"' --timeout 10m
# curl and check that the response is Hello
curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1 | grep Hello
# [docs:change-config]
juju config fastapi-hello-world greeting='Hi!'
# [docs:change-config-end]
# make sure that the application updates
juju wait-for application fastapi-hello-world --query='status=="maintenance"' --timeout 10m
juju wait-for application fastapi-hello-world --query='status=="active"' --timeout 10m
# curl and check that the response is now Hi
curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1 | grep Hi
cd ..
cat visitors_migrate.py >> migrate.py
cat visitors_app.py > app.py
sed -i "s/version: .*/version: 0.3/g" rockcraft.yaml
# [docs:docker-2nd-update]
ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
oci-archive:fastapi-hello-world_0.3_$(dpkg --print-architecture).rock \
docker://localhost:32000/fastapi-hello-world:0.3
# [docs:docker-2nd-update-end]
cat visitors_charmcraft.yaml >> ./charm/charmcraft.yaml
cd charm
# [docs:refresh-2nd-deployment]
CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack
juju refresh fastapi-hello-world \
--path=./fastapi-hello-world_$(dpkg --print-architecture).charm \
--resource app-image=localhost:32000/fastapi-hello-world:0.3
# [docs:refresh-2nd-deployment-end]
# [docs:deploy-postgres]
juju deploy postgresql-k8s --trust
juju integrate fastapi-hello-world postgresql-k8s
# [docs:deploy-postgres-end]
# give Juju some time to deploy and refresh the apps
juju wait-for application postgresql-k8s --query='status=="active"' --timeout 20m | juju status --relations
juju wait-for application fastapi-hello-world --query='status=="active"' --timeout 20m | juju status --relations
curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1 | grep Hi
curl http://fastapi-hello-world/visitors --resolve fastapi-hello-world:80:127.0.0.1 | grep 1
curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1 | grep Hi
curl http://fastapi-hello-world/visitors --resolve fastapi-hello-world:80:127.0.0.1 | grep 2
# Back out to main directory for cleanup
cd ..
# [docs:clean-environment]
# exit and delete the virtual environment
deactivate
rm -rf charm .venv __pycache__
# delete all the files created during the tutorial
rm fastapi-hello-world_0.1_$(dpkg --print-architecture).rock \
fastapi-hello-world_0.2_$(dpkg --print-architecture).rock \
fastapi-hello-world_0.3_$(dpkg --print-architecture).rock \
rockcraft.yaml app.py requirements.txt migrate.py
# Remove the juju model
juju destroy-model fastapi-hello-world --destroy-storage --no-prompt --force
# [docs:clean-environment-end]
34 changes: 34 additions & 0 deletions docs/tutorial/code/fastapi/visitors_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# FastAPI application that keeps track of visitors using a database

import datetime
import os
from typing import Annotated

from fastapi import FastAPI, Header
import psycopg2

app = FastAPI()
DATABASE_URI = os.environ["POSTGRESQL_DB_CONNECT_STRING"]


@app.get("/")
async def root(user_agent: Annotated[str | None, Header()] = None):
with psycopg2.connect(DATABASE_URI) as conn, conn.cursor() as cur:
timestamp = datetime.datetime.now()

cur.execute(
"INSERT INTO visitors (timestamp, user_agent) VALUES (%s, %s)",
(timestamp, user_agent)
)
conn.commit()

return {"message": os.getenv("APP_GREETING", "Hello World")}


@app.get("/visitors")
async def visitors():
with psycopg2.connect(DATABASE_URI) as conn, conn.cursor() as cur:
cur.execute("SELECT COUNT(*) FROM visitors")
total_visitors = cur.fetchone()[0]

return {"count": total_visitors}
6 changes: 6 additions & 0 deletions docs/tutorial/code/fastapi/visitors_charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# requires snippet for FastAPI application with a database

requires:
postgresql:
interface: postgresql_client
optional: false
21 changes: 21 additions & 0 deletions docs/tutorial/code/fastapi/visitors_migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Adds database to FastAPI application

import os

import psycopg2

DATABASE_URI = os.environ["POSTGRESQL_DB_CONNECT_STRING"]

def migrate():
with psycopg2.connect(DATABASE_URI) as conn, conn.cursor() as cur:
cur.execute("""
CREATE TABLE IF NOT EXISTS visitors (
timestamp TIMESTAMP NOT NULL,
user_agent TEXT NOT NULL
);
""")
conn.commit()


if __name__ == "__main__":
migrate()

0 comments on commit b1ddb79

Please sign in to comment.