Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Flask tutorial to Charmcraft RTD #1976

Open
wants to merge 75 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
2d2d5ba
Add Flask tutorial
erinecon Oct 29, 2024
1d8c5cd
nest in tutorial directory
erinecon Oct 29, 2024
ec84743
Add Flask tutorial to main index.rst
erinecon Oct 29, 2024
3917b92
Create task.yaml
erinecon Oct 29, 2024
3e6d311
add more commands to task.yaml
erinecon Oct 30, 2024
b073e1f
Add files via upload
erinecon Oct 30, 2024
b70ee97
Create requirements.txt
erinecon Oct 30, 2024
af2fc63
Create greeting_app.py
erinecon Oct 30, 2024
5e71d20
Create setup.rst
erinecon Oct 30, 2024
371ad5e
Update flask.rst
erinecon Oct 30, 2024
0ed76ed
fix lint issues in task.yaml
erinecon Oct 30, 2024
416e28a
Update task.yaml
erinecon Oct 30, 2024
4491fb5
Update task.yaml
erinecon Oct 30, 2024
5fdd47f
Replace code blocks with task.yaml snippets
erinecon Oct 30, 2024
dac9233
Update task.yaml
erinecon Oct 30, 2024
ecbbb94
Create visitors_app.py
erinecon Oct 30, 2024
73cc0fe
Update task.yaml
erinecon Oct 30, 2024
90bdb74
Create migrate.py
erinecon Oct 30, 2024
efc85d1
Replace more code with corresponding files
erinecon Oct 30, 2024
31536bb
Update setup.rst to fix linting issues
erinecon Oct 30, 2024
81377c0
Update visitors_app.py to remove trailing whitespace
erinecon Oct 30, 2024
b98a43a
Fix trailing whitespaces
erinecon Oct 30, 2024
f1db5a6
Fix indent issue in task.yaml
erinecon Oct 30, 2024
bb66b89
Add spread test for tutorial
erinecon Oct 30, 2024
9fba6d8
Update task.yaml
erinecon Oct 31, 2024
d377244
update spread Multipass VM and prepare instructions
erinecon Nov 12, 2024
9cbdf8e
update files for Flask tutorial spread test
erinecon Nov 12, 2024
eb66af3
Merge branch 'canonical:main' into main
erinecon Nov 12, 2024
70da76a
Merge branch 'main' into main
erinecon Nov 13, 2024
60d8da8
unset staging store env vars for flask spread test
erinecon Nov 13, 2024
314caaa
update Flask spread test to not use staging store
erinecon Nov 13, 2024
9f4dffa
update wait-for commands for debugging
erinecon Nov 14, 2024
c3c6952
Merge branch 'main' into main
erinecon Nov 14, 2024
187ac5f
set kill-timeout for flask test
erinecon Nov 14, 2024
29e6b0b
add microk8s status command to flask spread test
erinecon Nov 14, 2024
a51e987
Merge branch 'main' of https://github.com/erinecon/charmcraft
erinecon Nov 14, 2024
ecbf30b
Delete docs/tutorial/code/flask/migrate.py
erinecon Nov 15, 2024
5018e11
Run CI
erinecon Nov 15, 2024
c8bc347
update Juju version for Flask spread test
erinecon Nov 18, 2024
17921d7
update Juju version for Flask spread test
erinecon Nov 18, 2024
1e62911
update postgresql version for Flask spread test
erinecon Nov 18, 2024
66a9c41
Run CI
erinecon Nov 18, 2024
e3ab3ca
force update Juju version in Flask spread test
erinecon Nov 18, 2024
1a0df9b
move prepare section to task.yaml file
erinecon Nov 19, 2024
7e5d4ca
snap refresh juju
erinecon Nov 19, 2024
87cc035
remove fail flag from curl commands
erinecon Nov 19, 2024
0b3bdd6
add force and no-wait to destroy-model command
erinecon Nov 19, 2024
07a3506
remove force flag
erinecon Nov 20, 2024
2e2be05
explicitly set charmcraft env vars in environment section
erinecon Nov 20, 2024
a3db0da
revert changes for charmcraft env vars, specify base of nginx
erinecon Nov 20, 2024
381c9f3
Merge branch 'main' into main
erinecon Nov 21, 2024
dd2e254
specify revision of nginx
erinecon Nov 21, 2024
44dc02f
Merge branch 'main' of https://github.com/erinecon/charmcraft
erinecon Nov 21, 2024
66eac38
Merge branch 'main' into main
erinecon Nov 21, 2024
f4fa1a7
remove contributors from tutorial
erinecon Nov 22, 2024
9926ddc
changes from review
erinecon Nov 22, 2024
a781527
remove trailing whitespace
erinecon Nov 22, 2024
0ae0120
add tutorial index
erinecon Nov 22, 2024
1a8e45d
add tutorial/index to main index
erinecon Nov 22, 2024
f1e16ab
fix grep commands on curl requests
erinecon Nov 22, 2024
6e69855
add a wait-for for maintenance
erinecon Nov 22, 2024
3005bf2
reorder Diataxis sections
erinecon Nov 22, 2024
127b386
remove whitespace
erinecon Nov 22, 2024
6cac8cb
add some forgotten greps
erinecon Nov 25, 2024
8ddb841
remove note about constraints
erinecon Nov 25, 2024
210dcaa
formatting changes
erinecon Nov 25, 2024
1e23891
add preamble sentence
erinecon Nov 25, 2024
409ae62
update file name in tutorial
erinecon Nov 25, 2024
393226b
change period to a comma
erinecon Nov 25, 2024
8c65a89
add more spaces to yaml snippet
erinecon Nov 25, 2024
3985020
change charmcraft version to latest/stable
erinecon Nov 27, 2024
1a88669
replace setup.rst with setup_stable.rst
erinecon Nov 27, 2024
ed25c8c
update setup filename
erinecon Nov 27, 2024
f920c2a
change nginx deploy to specify base instead of revision
erinecon Nov 27, 2024
7874309
trigger CI
erinecon Nov 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Most of Charmcraft's documentation is available there.
:maxdepth: 1
:hidden:

tutorial/index
howto/index
reference/index
explanation/index
Expand Down
75 changes: 75 additions & 0 deletions docs/reuse/tutorial/setup_stable.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Install Multipass.

.. seealso::

See more: `Multipass | How to install Multipass
<https://multipass.run/docs/install-multipass>`_

Use Multipass to launch an Ubuntu VM with the name ``charm-dev``
from the 24.04 blueprint:

.. code-block:: bash

multipass launch --cpus 4 --disk 50G --memory 4G --name charm-dev 24.04

Once the VM is up, open a shell into it:

.. code-block:: bash

multipass shell charm-dev

In order to create the rock, you'll need to install Rockcraft:

.. code-block:: bash

sudo snap install rockcraft --classic

``LXD`` will be required for building the rock.
Make sure it is installed and initialised:

.. code-block:: bash

sudo snap install lxd
lxd init --auto

In order to create the charm, you'll need to install Charmcraft:

.. code-block:: bash

sudo snap install charmcraft --channel latest/stable --classic

.. warning::

This tutorial requires version ``3.0.0`` or later of Charmcraft. Check the
version of Charmcraft using ``charmcraft --version`` If you have an older
version of Charmcraft installed, use
``sudo snap refresh charmcraft --channel latest/edge`` to get the latest
edge version of Charmcraft.

MicroK8s is required to deploy the Flask application on Kubernetes. Install MicroK8s:

.. code-block:: bash

sudo snap install microk8s --channel 1.31-strict/stable
sudo adduser $USER snap_microk8s
newgrp snap_microk8s

Wait for MicroK8s to be ready using ``sudo microk8s status --wait-ready``.
Several MicroK8s add-ons are required for deployment:

.. code-block:: bash

sudo microk8s enable hostpath-storage
# Required to host the OCI image of the Flask application
sudo microk8s enable registry
# Required to expose the Flask application
sudo microk8s enable ingress

Juju is required to deploy the Flask application.
Install Juju and bootstrap a development controller:

.. code-block:: bash

sudo snap install juju --channel 3.5/stable
mkdir -p ~/.local/share
juju bootstrap microk8s dev-controller
12 changes: 12 additions & 0 deletions docs/tutorial/code/flask/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import flask

app = flask.Flask(__name__)


@app.route("/")
def index():
return "Hello, world!\n"


if __name__ == "__main__":
app.run()
14 changes: 14 additions & 0 deletions docs/tutorial/code/flask/greeting_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import flask

app = flask.Flask(__name__)
app.config.from_prefixed_env()


@app.route("/")
def index():
greeting = app.config.get("GREETING", "Hello, world!")
return f"{greeting}\n"


if __name__ == "__main__":
app.run()
7 changes: 7 additions & 0 deletions docs/tutorial/code/flask/greeting_charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
config:
options:
greeting:
description: |
The greeting to be returned by the Flask application.
default: "Hello, world!"
type: string
1 change: 1 addition & 0 deletions docs/tutorial/code/flask/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Flask
218 changes: 218 additions & 0 deletions docs/tutorial/code/flask/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
###########################################
# IMPORTANT
# Comments matter!
# The docs use the wrapping comments as
# markers for including said instructions
# as snippets in the docs.
###########################################
summary: Getting started with Flask tutorial

kill-timeout: 90m

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

# Add setup instructions
# (Ran into issues in prepare section)
snap install rockcraft --classic
snap install lxd
lxd init --auto
snap refresh charmcraft --channel=latest/edge --amend
snap install microk8s --channel=1.31-strict/stable
snap refresh juju --channel=3.5/stable --amend

# Juju config setup
lxc network set lxdbr0 ipv6.address none
mkdir -p ~/.local/share

# 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
pip install -r requirements.txt
# [docs:create-venv-end]

flask run -p 8000 &
retry -n 5 --wait 2 curl --fail localhost:8000

# [docs:curl-flask]
curl localhost:8000
# [docs:curl-flask-end]

kill $!

# [docs:create-rockcraft-yaml]
rockcraft init --profile flask-framework
# [docs:create-rockcraft-yaml-end]

sed -i "s/name: .*/name: flask-hello-world/g" rockcraft.yaml
sed -i "s/amd64/$(dpkg --print-architecture)/g" rockcraft.yaml

# [docs:pack]
rockcraft pack
# [docs:pack-end]

# [docs:ls-rock]
ls *.rock -l
# [docs:ls-rock-end]

# [docs:skopeo-copy]
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
oci-archive:flask-hello-world_0.1_$(dpkg --print-architecture).rock \
docker://localhost:32000/flask-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 flask-framework --name flask-hello-world
# [docs:charm-init-end]

# [docs:charm-pack]
charmcraft pack
# [docs:charm-pack-end]

# [docs:ls-charm]
ls *.charm -l
# [docs:ls-charm-end]

# [docs:add-juju-model]
juju add-model flask-hello-world
# [docs:add-juju-model-end]

juju set-model-constraints -m flask-hello-world arch=$(dpkg --print-architecture)

# [docs:deploy-juju-model]
juju deploy ./flask-hello-world_ubuntu-22.04-$(dpkg --print-architecture).charm \
flask-hello-world --resource \
flask-app-image=localhost:32000/flask-hello-world:0.1
# [docs:deploy-juju-model-end]

# [docs:deploy-nginx]
juju deploy nginx-ingress-integrator --channel=latest/edge --base ubuntu@20.04
juju integrate nginx-ingress-integrator flask-hello-world
# [docs:deploy-nginx-end]

# [docs:config-nginx]
juju config nginx-ingress-integrator \
service-hostname=flask-hello-world path-routes=/
# [docs:config-nginx-end]

# give Juju some time to deploy the apps
juju wait-for application flask-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://flask-hello-world --resolve flask-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
rockcraft pack

# [docs:docker-update]
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
oci-archive:flask-hello-world_0.2_$(dpkg --print-architecture).rock \
docker://localhost:32000/flask-hello-world:0.2
# [docs:docker-update-end]

cat greeting_charmcraft.yaml >> ./charm/charmcraft.yaml
cd charm
charmcraft pack

# [docs:refresh-deployment]
juju refresh flask-hello-world \
--path=./flask-hello-world_ubuntu-22.04-$(dpkg --print-architecture).charm \
--resource flask-app-image=localhost:32000/flask-hello-world:0.2
# [docs:refresh-deployment-end]

# give Juju some time to refresh the app
juju wait-for application flask-hello-world --query='status=="active"' --timeout 10m

# curl and check that the response is Hello
curl http://flask-hello-world --resolve flask-hello-world:80:127.0.0.1 | grep Hello

# [docs:change-config]
juju config flask-hello-world greeting='Hi!'
# [docs:change-config-end]

# make sure that the application updates
juju wait-for application flask-hello-world --query='status=="maintenance"' --timeout 10m
juju wait-for application flask-hello-world --query='status=="active"' --timeout 10m

# curl and check that the response is now Hi
curl http://flask-hello-world --resolve flask-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
echo "psycopg2-binary" >> requirements.txt
rockcraft pack

# [docs:docker-2nd-update]
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
oci-archive:flask-hello-world_0.3_$(dpkg --print-architecture).rock \
docker://localhost:32000/flask-hello-world:0.3
# [docs:docker-2nd-update-end]

cat visitors_charmcraft.yaml >> ./charm/charmcraft.yaml
cd charm
charmcraft pack

# [docs:refresh-2nd-deployment]
juju refresh flask-hello-world \
--path=./flask-hello-world_ubuntu-22.04-$(dpkg --print-architecture).charm \
--resource flask-app-image=localhost:32000/flask-hello-world:0.3
# [docs:refresh-2nd-deployment-end]

# [docs:deploy-postgres]
juju deploy postgresql-k8s --trust
juju integrate flask-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 10m
juju wait-for application flask-hello-world --query='status=="active"' --timeout 10m

curl http://flask-hello-world --resolve flask-hello-world:80:127.0.0.1 | grep Hi
curl http://flask-hello-world/visitors --resolve flask-hello-world:80:127.0.0.1 | grep 1
curl http://flask-hello-world --resolve flask-hello-world:80:127.0.0.1 | grep Hi
curl http://flask-hello-world/visitors --resolve flask-hello-world:80:127.0.0.1 | grep 2

# Back out to main directory for clean-up
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 flask-hello-world_0.1_$(dpkg --print-architecture).rock flask-hello-world_0.2_$(dpkg --print-architecture).rock \
flask-hello-world_0.3_$(dpkg --print-architecture).rock rockcraft.yaml app.py \
requirements.txt migrate.py
# Remove the juju model
juju destroy-model flask-hello-world --destroy-storage --no-prompt --force
# [docs:clean-environment-end]
40 changes: 40 additions & 0 deletions docs/tutorial/code/flask/visitors_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import datetime
import os

import flask
import psycopg2

app = flask.Flask(__name__)
app.config.from_prefixed_env()

DATABASE_URI = os.environ["POSTGRESQL_DB_CONNECT_STRING"]


@app.route("/")
def index():
with psycopg2.connect(DATABASE_URI) as conn, conn.cursor() as cur:
user_agent = flask.request.headers.get('User-Agent')
timestamp = datetime.datetime.now()

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


greeting = app.config.get("GREETING", "Hello, world!")
return f"{greeting}\n"


@app.route("/visitors")
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 f"{total_visitors}\n"


if __name__ == "__main__":
app.run()
4 changes: 4 additions & 0 deletions docs/tutorial/code/flask/visitors_charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
requires:
postgresql:
interface: postgresql_client
optional: false
Loading
Loading