diff --git a/docs/.wordlist.txt b/docs/.wordlist.txt index 4be14692e..548774edd 100644 --- a/docs/.wordlist.txt +++ b/docs/.wordlist.txt @@ -69,6 +69,7 @@ LTS LXD lxd Multipass +multipass nano NGINX nginx diff --git a/docs/how-to/code/use-django-extension/example/example_django/example_django/asgi.py b/docs/how-to/code/use-django-extension/example/example_django/example_django/asgi.py deleted file mode 100644 index a25bac07c..000000000 --- a/docs/how-to/code/use-django-extension/example/example_django/example_django/asgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -ASGI config for example_django project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ -""" - -import os - -from django.core.asgi import get_asgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_django.settings") - -application = get_asgi_application() diff --git a/docs/how-to/code/use-django-extension/example/example_django/example_django/settings.py b/docs/how-to/code/use-django-extension/example/example_django/example_django/settings.py deleted file mode 100644 index 8ad22fb91..000000000 --- a/docs/how-to/code/use-django-extension/example/example_django/example_django/settings.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Django settings for example_django project. - -Generated by 'django-admin startproject' using Django 5.0. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.0/ref/settings/ -""" - -from pathlib import Path - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-64wlq26vpv0ah(v1%t@l*yljw$lekb!o4a$us+wu0!x-^llqt(" - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", -] - -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", -] - -ROOT_URLCONF = "example_django.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], - "APP_DIRS": True, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - ], - }, - }, -] - -WSGI_APPLICATION = "example_django.wsgi.application" - - -# Database -# https://docs.djangoproject.com/en/5.0/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - } -} - - -# Password validation -# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/5.0/topics/i18n/ - -LANGUAGE_CODE = "en-us" - -TIME_ZONE = "UTC" - -USE_I18N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.0/howto/static-files/ - -STATIC_URL = "static/" - -# Default primary key field type -# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/docs/how-to/code/use-django-extension/example/example_django/example_django/urls.py b/docs/how-to/code/use-django-extension/example/example_django/example_django/urls.py deleted file mode 100644 index 0f39d3c26..000000000 --- a/docs/how-to/code/use-django-extension/example/example_django/example_django/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -URL configuration for example_django project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/5.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" - -from django.contrib import admin -from django.urls import path - -urlpatterns = [ - path("admin/", admin.site.urls), -] diff --git a/docs/how-to/code/use-django-extension/example/example_django/example_django/wsgi.py b/docs/how-to/code/use-django-extension/example/example_django/example_django/wsgi.py deleted file mode 100644 index 36a83afcb..000000000 --- a/docs/how-to/code/use-django-extension/example/example_django/example_django/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for example_django project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_django.settings") - -application = get_wsgi_application() diff --git a/docs/how-to/code/use-django-extension/example/example_django/manage.py b/docs/how-to/code/use-django-extension/example/example_django/manage.py deleted file mode 100755 index 77dced937..000000000 --- a/docs/how-to/code/use-django-extension/example/example_django/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_django.settings") - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == "__main__": - main() diff --git a/docs/how-to/code/use-django-extension/example/requirements.txt b/docs/how-to/code/use-django-extension/example/requirements.txt deleted file mode 100644 index 68d357cf8..000000000 --- a/docs/how-to/code/use-django-extension/example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -django \ No newline at end of file diff --git a/docs/how-to/code/use-django-extension/example/rockcraft.yaml b/docs/how-to/code/use-django-extension/example/rockcraft.yaml deleted file mode 100644 index 1e831b2a2..000000000 --- a/docs/how-to/code/use-django-extension/example/rockcraft.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: example-django -summary: A Django application -description: A rock packing a Django application -version: "0.1" -base: ubuntu@22.04 -license: Apache-2.0 - -extensions: - - django-framework - -platforms: - amd64: - arm64: diff --git a/docs/how-to/code/use-django-extension/override_example/foobar/asgi.py b/docs/how-to/code/use-django-extension/override_example/foobar/asgi.py deleted file mode 100644 index a7dd64eb9..000000000 --- a/docs/how-to/code/use-django-extension/override_example/foobar/asgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -ASGI config for foobar project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ -""" - -import os - -from django.core.asgi import get_asgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings") - -application = get_asgi_application() diff --git a/docs/how-to/code/use-django-extension/override_example/foobar/settings.py b/docs/how-to/code/use-django-extension/override_example/foobar/settings.py deleted file mode 100644 index 135c6a4a4..000000000 --- a/docs/how-to/code/use-django-extension/override_example/foobar/settings.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Django settings for foobar project. - -Generated by 'django-admin startproject' using Django 5.0. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.0/ref/settings/ -""" - -from pathlib import Path - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-q^h4op%sov*%=rw!!h&9_p5ym28oxrym7#r-sfc&6jka(6gkbj" - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", -] - -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", -] - -ROOT_URLCONF = "foobar.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], - "APP_DIRS": True, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - ], - }, - }, -] - -WSGI_APPLICATION = "foobar.wsgi.application" - - -# Database -# https://docs.djangoproject.com/en/5.0/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - } -} - - -# Password validation -# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/5.0/topics/i18n/ - -LANGUAGE_CODE = "en-us" - -TIME_ZONE = "UTC" - -USE_I18N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.0/howto/static-files/ - -STATIC_URL = "static/" - -# Default primary key field type -# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/docs/how-to/code/use-django-extension/override_example/foobar/urls.py b/docs/how-to/code/use-django-extension/override_example/foobar/urls.py deleted file mode 100644 index b0fb08230..000000000 --- a/docs/how-to/code/use-django-extension/override_example/foobar/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -URL configuration for foobar project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/5.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" - -from django.contrib import admin -from django.urls import path - -urlpatterns = [ - path("admin/", admin.site.urls), -] diff --git a/docs/how-to/code/use-django-extension/override_example/foobar/wsgi.py b/docs/how-to/code/use-django-extension/override_example/foobar/wsgi.py deleted file mode 100644 index d3630abd5..000000000 --- a/docs/how-to/code/use-django-extension/override_example/foobar/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for foobar project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings") - -application = get_wsgi_application() diff --git a/docs/how-to/code/use-django-extension/override_example/manage.py b/docs/how-to/code/use-django-extension/override_example/manage.py deleted file mode 100755 index 254226317..000000000 --- a/docs/how-to/code/use-django-extension/override_example/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings") - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == "__main__": - main() diff --git a/docs/how-to/code/use-django-extension/override_example/requirements.txt b/docs/how-to/code/use-django-extension/override_example/requirements.txt deleted file mode 100644 index 68d357cf8..000000000 --- a/docs/how-to/code/use-django-extension/override_example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -django \ No newline at end of file diff --git a/docs/how-to/code/use-django-extension/override_example/rockcraft.yaml b/docs/how-to/code/use-django-extension/override_example/rockcraft.yaml deleted file mode 100644 index 7908ae21a..000000000 --- a/docs/how-to/code/use-django-extension/override_example/rockcraft.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: example-django -summary: A Django application -description: A rock packing a Django application -version: "0.1" -base: ubuntu@22.04 -license: Apache-2.0 - -extensions: - - django-framework - -platforms: - amd64: - arm64: - -services: - django: - command: /bin/python3 -m gunicorn -c /django/gunicorn.conf.py foobar.wsgi:application - -# [docs:parts-start] -parts: - django-framework/install-app: - plugin: dump - source: . - organize: - foobar: django/app/foobar - manage.py: django/app/manage.py - stage: - - django/app/foobar - - django/app/manage.py - prime: - - django/app/foobar - - django/app/manage.py -# [docs:parts-end] diff --git a/docs/how-to/code/use-django-extension/task.yaml b/docs/how-to/code/use-django-extension/task.yaml deleted file mode 100644 index bb03f0d17..000000000 --- a/docs/how-to/code/use-django-extension/task.yaml +++ /dev/null @@ -1,22 +0,0 @@ -########################################### -# IMPORTANT -# Comments matter! -# The docs use the wrapping comments as -# markers for including said instructions -# as snippets in the docs. -########################################### -summary: test the "Use the django-framework extension" guide - -environment: - ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS: "1" - EXAMPLE_DIR/example: example - EXAMPLE_DIR/override_example: override_example - -execute: | - cd $EXAMPLE_DIR - rockcraft pack --verbosity debug - -restore: | - cd $EXAMPLE_DIR - rm *.rock - rockcraft clean diff --git a/docs/how-to/code/use-flask-extension/example/app.py b/docs/how-to/code/use-flask-extension/example/app.py deleted file mode 100644 index 062711083..000000000 --- a/docs/how-to/code/use-flask-extension/example/app.py +++ /dev/null @@ -1,3 +0,0 @@ -import flask - -app = flask.Flask(__name__) diff --git a/docs/how-to/code/use-flask-extension/example/requirements.txt b/docs/how-to/code/use-flask-extension/example/requirements.txt deleted file mode 100644 index 8ab6294c6..000000000 --- a/docs/how-to/code/use-flask-extension/example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flask \ No newline at end of file diff --git a/docs/how-to/code/use-flask-extension/example/rockcraft.yaml b/docs/how-to/code/use-flask-extension/example/rockcraft.yaml deleted file mode 100644 index 51454a31c..000000000 --- a/docs/how-to/code/use-flask-extension/example/rockcraft.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: example-flask -summary: A Flask application -description: A rock packing a Flask application via the flask extension -version: "0.1" -base: bare -build-base: ubuntu@22.04 -license: Apache-2.0 - -extensions: - - flask-framework - -platforms: - amd64: diff --git a/docs/how-to/code/use-flask-extension/prime_example/.env.production b/docs/how-to/code/use-flask-extension/prime_example/.env.production deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/how-to/code/use-flask-extension/prime_example/app.py b/docs/how-to/code/use-flask-extension/prime_example/app.py deleted file mode 100644 index 062711083..000000000 --- a/docs/how-to/code/use-flask-extension/prime_example/app.py +++ /dev/null @@ -1,3 +0,0 @@ -import flask - -app = flask.Flask(__name__) diff --git a/docs/how-to/code/use-flask-extension/prime_example/requirements.txt b/docs/how-to/code/use-flask-extension/prime_example/requirements.txt deleted file mode 100644 index 8ab6294c6..000000000 --- a/docs/how-to/code/use-flask-extension/prime_example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flask \ No newline at end of file diff --git a/docs/how-to/code/use-flask-extension/prime_example/rockcraft.yaml b/docs/how-to/code/use-flask-extension/prime_example/rockcraft.yaml deleted file mode 100644 index f79b936aa..000000000 --- a/docs/how-to/code/use-flask-extension/prime_example/rockcraft.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: example-flask -summary: A Flask application -description: A rock packing a Flask application via the flask extension -version: "0.1" -base: bare -build-base: ubuntu@22.04 -license: Apache-2.0 - -extensions: - - flask-framework - -platforms: - amd64: - -# [docs:parts-start] -parts: - flask-framework/install-app: - prime: - - flask/app/static - - flask/app/.env.production - - flask/app/app.py - - flask/app/templates -# [docs:parts-end] diff --git a/docs/how-to/code/use-flask-extension/prime_example/static/example.js b/docs/how-to/code/use-flask-extension/prime_example/static/example.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/how-to/code/use-flask-extension/prime_example/templates/example.html b/docs/how-to/code/use-flask-extension/prime_example/templates/example.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/how-to/code/use-flask-extension/prime_exclude_example/app.py b/docs/how-to/code/use-flask-extension/prime_exclude_example/app.py deleted file mode 100644 index 062711083..000000000 --- a/docs/how-to/code/use-flask-extension/prime_exclude_example/app.py +++ /dev/null @@ -1,3 +0,0 @@ -import flask - -app = flask.Flask(__name__) diff --git a/docs/how-to/code/use-flask-extension/prime_exclude_example/requirements.txt b/docs/how-to/code/use-flask-extension/prime_exclude_example/requirements.txt deleted file mode 100644 index 8ab6294c6..000000000 --- a/docs/how-to/code/use-flask-extension/prime_exclude_example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -flask \ No newline at end of file diff --git a/docs/how-to/code/use-flask-extension/prime_exclude_example/rockcraft.yaml b/docs/how-to/code/use-flask-extension/prime_exclude_example/rockcraft.yaml deleted file mode 100644 index a0b025818..000000000 --- a/docs/how-to/code/use-flask-extension/prime_exclude_example/rockcraft.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: example-flask -summary: A Flask application -description: A rock packing a Flask application via the flask extension -version: "0.1" -base: bare -build-base: ubuntu@22.04 -license: Apache-2.0 - -extensions: - - flask-framework - -platforms: - amd64: - -# [docs:parts-start] -parts: - flask-framework/install-app: - prime: - - -flask/app/.git - - -flask/app/.venv - - -flask/app/.yarn - - -flask/app/node_modules -# [docs:parts-end] diff --git a/docs/how-to/code/use-flask-extension/task.yaml b/docs/how-to/code/use-flask-extension/task.yaml deleted file mode 100644 index 44736e382..000000000 --- a/docs/how-to/code/use-flask-extension/task.yaml +++ /dev/null @@ -1,22 +0,0 @@ -########################################### -# IMPORTANT -# Comments matter! -# The docs use the wrapping comments as -# markers for including said instructions -# as snippets in the docs. -########################################### -summary: test the "Use the flask-framework extension" guide - -environment: - ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS: "1" - -execute: | - cd example - rockcraft pack --verbosity debug - cd ../prime_example - rockcraft pack --verbosity debug - cd ../prime_exclude_example - rockcraft pack --verbosity debug - -restore: | - rm -rf */*.rock diff --git a/docs/how-to/rocks/index.rst b/docs/how-to/rocks/index.rst index 931b7def7..24c75c7e6 100644 --- a/docs/how-to/rocks/index.rst +++ b/docs/how-to/rocks/index.rst @@ -8,7 +8,5 @@ Publish a rock to a registry Migrate a Docker image to a chiselled rock Chisel an existing rock - Use the flask extension - Use the django extension Include local and remote files Override a plugin's build diff --git a/docs/how-to/rocks/use-django-extension.rst b/docs/how-to/rocks/use-django-extension.rst deleted file mode 100644 index 8da7ba977..000000000 --- a/docs/how-to/rocks/use-django-extension.rst +++ /dev/null @@ -1,49 +0,0 @@ -Use the django-framework extension ----------------------------------- - -.. note:: - The Django extension is compatible with the ``bare`` and ``ubuntu@22.04`` - bases. - -To use it, include ``extensions: [ django-framework ]`` in your -``rockcraft.yaml`` file. - -Example: - -.. literalinclude:: ../code/use-django-extension/example/rockcraft.yaml - :language: yaml - -Managing project files with the Django extension ------------------------------------------------- - -The extension will search for a directory named after the rock within the -Rockcraft project directory to transfer it into the rock image. The Django -project should have a directory named after the rock, and the ``wsgi.py`` -file within this directory must contain an object named ``application`` -to serve as the WSGI entry point. - -The following is a typical Rockcraft project that meets this requirement. - -.. code-block:: - - +-- example_django - | |-- example_django - | | |-- wsgi.py - | | +-- ... - | |-- manage.py - | |-- migrate.sh - | +-- some_app - | |-- views.py - | +-- ... - |-- requirements.txt - +-- rockcraft.yaml - -To override this behaviour and adopt a different project structure, add -the ``django-framework/install-app`` part to install the Django project in -the ``/django/app`` directory within the rock image and update the command -for the ``django`` service to point to the WSGI path of your project. - -.. literalinclude:: ../code/use-django-extension/override_example/rockcraft.yaml - :language: yaml - :start-after: [docs:parts-start] - :end-before: [docs:parts-end] diff --git a/docs/how-to/rocks/use-flask-extension.rst b/docs/how-to/rocks/use-flask-extension.rst deleted file mode 100644 index 3c86541ed..000000000 --- a/docs/how-to/rocks/use-flask-extension.rst +++ /dev/null @@ -1,42 +0,0 @@ -Use the flask-framework extension ---------------------------------- - -.. note:: - The Flask extension is compatible with the ``bare`` and ``ubuntu@22.04`` - bases. - -To employ it, include ``extensions: [ flask-framework ]`` in your -``rockcraft.yaml`` file. - -Example: - -.. literalinclude:: ../code/use-flask-extension/example/rockcraft.yaml - :language: yaml - -Managing project files with the flask extension ------------------------------------------------ - -By default the flask extension only includes ``app.py``, ``static/``, -``app/``, and ``templates/`` in the flask project, but you can overwrite this -behaviour with a prime declaration in the specially-named -``flask-framework/install-app`` part to instruct the flask extension on which -files to include or exclude from the project directory in the rock image. - -The extension places the files from the project folder in the ``/flask/app`` -directory in the final image - therefore, all inclusions and exclusions must -be prefixed with ``flask/app``. - -For example, to include only select files: - -.. literalinclude:: ../code/use-flask-extension/prime_example/rockcraft.yaml - :language: yaml - :start-after: [docs:parts-start] - :end-before: [docs:parts-end] - -To exclude certain files from the project directory in the rock image, -add the following part to ``rockcraft.yaml``: - -.. literalinclude:: ../code/use-flask-extension/prime_exclude_example/rockcraft.yaml - :language: yaml - :start-after: [docs:parts-start] - :end-before: [docs:parts-end] diff --git a/docs/reference/extensions.rst b/docs/reference/extensions.rst deleted file mode 100644 index 72845a485..000000000 --- a/docs/reference/extensions.rst +++ /dev/null @@ -1,25 +0,0 @@ -Extensions -********** - -Just as the Snapcraft extensions are designed to simplify Snap creation, -Rockcraft extensions are crafted to expand and modify the user-provided -rockcraft project file, aiming to minimise the boilerplate code when -initiating a new rock. - -The ``flask-framework`` extension ---------------------------------- - -The Flask extension streamlines the process of building Flask application rocks. - -It facilitates the installation of Flask application dependencies, including -Gunicorn, in the rock image. Additionally, it transfers your project files to -``/flask/app`` within the rock image. - -A statsd-exporter is installed alongside the Gunicorn server to export Gunicorn -server metrics. - -The ``django-framework`` extension ----------------------------------- - -The Django extension is similar to the flask-framework extension but tailored -for Django applications. diff --git a/docs/reference/extensions/flask-framework.rst b/docs/reference/extensions/flask-framework.rst new file mode 100644 index 000000000..5b5115f41 --- /dev/null +++ b/docs/reference/extensions/flask-framework.rst @@ -0,0 +1,84 @@ +.. _flask-framework-reference: + +flask-framework +--------------- + +The Flask extension streamlines the process of building Flask application rocks. + +It facilitates the installation of Flask application dependencies, including +Gunicorn, inside the rock. Additionally, it transfers your project files to +``/flask/app`` within the rock. + +A statsd-exporter is installed alongside the Gunicorn server to export Gunicorn +server metrics. + +.. note:: + The Flask extension is compatible with the ``bare`` and ``ubuntu@22.04`` + bases. + +Project requirements +==================== + +There are 2 requirements to be able to use the ``flask-framework`` extension: + +1. There must be a ``requirements.txt`` file in the root of the project with + ``Flask`` declared as a dependency +2. The project must include a WSGI app with the path ``app:app``. This means + there must be an ``app.py`` file at the root of the project with the name + of the Flask object is set to ``app`` + +``parts`` > ``flask-framework/runtime-slices`` > ``stage-packages`` +=================================================================== + +You can use this field to specify any :ref:`chisel_explanation` package slices +required for your Flask +application. For example, below we use it to specify ``libxml2_libs``: + +.. code-block:: yaml + + parts: + flask-framework/runtime-slices: + stage-packages: + # list required Chisel package slices for your flask application below. + - libxml2_libs + +``parts`` > ``flask-framework/runtime-debs`` > ``stage-packages`` +================================================================= + +You can use this field to specify any Debian packages required for your Flask +application. For example, below we use it to specify ``libpq-dev``: + +.. code-block:: yaml + + parts: + flask-framework/runtime-debs: + stage-packages: + # list required Debian packages for your flask application below. + - libpq-dev + +``parts`` > ``flask-framework/install-app`` > ``prime`` +======================================================= + +You can use this field to specify the files to be included or excluded from +your rock upon ``rockcraft pack``. Follow the ``flask/app/`` +notation. For example: + +.. code-block:: yaml + + parts: + flask-framework/install-app: + prime: + - flask/app/.env + - flask/app/app.py + - flask/app/webapp + - flask/app/templates + - flask/app/static + +Some files, if they exist, are included by default. These include: +``app``, ``app.py``, ``migrate``, ``migrate.sh``, ``migrate.py``, ``static``, +``templates``. + +Useful links +============ + +- :ref:`build-a-rock-for-a-flask-application` diff --git a/docs/reference/extensions/index.rst b/docs/reference/extensions/index.rst new file mode 100644 index 000000000..05a0563a5 --- /dev/null +++ b/docs/reference/extensions/index.rst @@ -0,0 +1,14 @@ +.. _extensions: + +Extensions +********** + +Just as the Snapcraft extensions are designed to simplify Snap creation, +Rockcraft extensions are crafted to expand and modify the user-provided +rockcraft project file, aiming to minimise the boilerplate code when +initiating a new rock. + +.. toctree:: + :maxdepth: 1 + + flask-framework diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 09030578a..3d22c37a1 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -12,6 +12,6 @@ Rockcraft's components, commands and keywords. rockcraft.yaml commands - extensions + Extensions plugins parts_steps diff --git a/docs/reuse/links.txt b/docs/reuse/links.txt index 6e63bb94d..2a21005ec 100644 --- a/docs/reuse/links.txt +++ b/docs/reuse/links.txt @@ -5,7 +5,7 @@ .. _`OCI archive format`: https://github.com/opencontainers/image-spec/blob/main/layer.md#distributable-format .. _OCI_image_spec: https://github.com/opencontainers/image-spec/blob/main/spec.md .. _`OCI layers`: https://github.com/opencontainers/image-spec/blob/main/layer.md -.. _LXD: https://linuxcontainers.org/lxd/docs/latest/ +.. _LXD: https://canonical.com/lxd .. _Multipass: https://multipass.run/docs .. _`Open Container Initiative`: https://opencontainers.org/ .. _skopeo: https://github.com/containers/skopeo diff --git a/docs/tutorials/code/getting-started-with-flask/app.py b/docs/tutorials/code/getting-started-with-flask/app.py new file mode 100644 index 000000000..2ef2f596e --- /dev/null +++ b/docs/tutorials/code/getting-started-with-flask/app.py @@ -0,0 +1,12 @@ +import flask + +app = flask.Flask(__name__) + + +@app.route("/") +def index(): + return "Hello, world!\n" + + +if __name__ == "__main__": + app.run() diff --git a/docs/tutorials/code/getting-started-with-flask/requirements.txt b/docs/tutorials/code/getting-started-with-flask/requirements.txt new file mode 100644 index 000000000..e3e9a71d9 --- /dev/null +++ b/docs/tutorials/code/getting-started-with-flask/requirements.txt @@ -0,0 +1 @@ +Flask diff --git a/docs/tutorials/code/getting-started-with-flask/task.yaml b/docs/tutorials/code/getting-started-with-flask/task.yaml new file mode 100644 index 000000000..cf355d405 --- /dev/null +++ b/docs/tutorials/code/getting-started-with-flask/task.yaml @@ -0,0 +1,127 @@ +########################################### +# 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 + +environment: + +execute: | + # [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] + + # [docs:run-flask] + flask run -p 8000 & + # [docs:run-flask-end] + retry -n 5 --wait 2 curl localhost:8000 + + # [docs:curl-flask] + curl localhost:8000 + # [docs:curl-flask-end] + + # [docs:stop-flask] + kill $! + # [docs:stop-flask-end] + + # [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 + + # [docs:pack] + rockcraft pack + # [docs:pack-end] + + # [docs:ls-rock] + ls *.rock -l --block-size=MB + # [docs:ls-rock-end] + + # [docs:skopeo-copy] + sudo rockcraft.skopeo --insecure-policy copy oci-archive:flask-hello-world_0.1_amd64.rock docker-daemon:flask-hello-world:0.1 + # [docs:skopeo-copy-end] + + # [docs:docker-images] + sudo docker images flask-hello-world:0.1 + # [docs:docker-images-end] + + # [docs:docker-run] + sudo docker run --rm -d -p 8000:8000 --name flask-hello-world flask-hello-world:0.1 + # [docs:docker-run-end] + retry -n 5 --wait 2 curl localhost:8000 + + # [docs:curl-flask-rock] + curl localhost:8000 + # [docs:curl-flask-rock-end] + + # [docs:get-logs] + sudo docker exec flask-hello-world pebble logs flask + # [docs:get-logs-end] + + # [docs:stop-docker] + sudo docker stop flask-hello-world + sudo docker rmi flask-hello-world:0.1 + # [docs:stop-docker-end] + + # [docs:change-base] + sed -i "s/base: .*/base: bare\nbuild-base: ubuntu@22.04/g" rockcraft.yaml + # [docs:change-base-end] + + # [docs:chisel-pack] + rockcraft pack + # [docs:chisel-pack-end] + + # [docs:ls-bare-rock] + ls *.rock -l --block-size=MB + # [docs:ls-bare-rock-end] + + # [docs:docker-run-chisel] + sudo rockcraft.skopeo --insecure-policy copy oci-archive:flask-hello-world_0.1_amd64.rock docker-daemon:flask-hello-world:0.1 + sudo docker images flask-hello-world:0.1 + sudo docker run --rm -d -p 8000:8000 --name flask-hello-world flask-hello-world:0.1 + # [docs:docker-run-chisel-end] + retry -n 5 --wait 2 curl localhost:8000 + + # [docs:curl-flask-bare-rock] + curl localhost:8000 + # [docs:curl-flask-bare-rock-end] + + # [docs:stop-docker-chisel] + sudo docker stop flask-hello-world + sudo docker rmi flask-hello-world:0.1 + # [docs:stop-docker-chisel-end] + + cat time-app.py > app.py + sed -i "s/version: .*/version: 0.2/g" rockcraft.yaml + rockcraft clean + + # [docs:docker-run-update] + rockcraft pack + sudo rockcraft.skopeo --insecure-policy copy oci-archive:flask-hello-world_0.2_amd64.rock docker-daemon:flask-hello-world:0.2 + sudo docker images flask-hello-world:0.2 + sudo docker run --rm -d -p 8000:8000 --name flask-hello-world flask-hello-world:0.2 + # [docs:docker-run-update-end] + retry -n 5 --wait 2 curl localhost:8000/time + + # [docs:curl-time] + curl localhost:8000/time + # [docs:curl-time-end] + + # [docs:stop-docker-updated] + sudo docker stop flask-hello-world + sudo docker rmi flask-hello-world:0.2 + # [docs:stop-docker-updated-end] + + # [docs:cleanup] + # exit and delete the virtual environment + deactivate + rm -rf .venv + # delete all the files created during the tutorial + rm flask-hello-world_0.1_amd64.rock flask-hello-world_0.2_amd64.rock rockcraft.yaml app.py requirements.txt + # [docs:cleanup-end] diff --git a/docs/tutorials/code/getting-started-with-flask/time-app.py b/docs/tutorials/code/getting-started-with-flask/time-app.py new file mode 100644 index 000000000..2c0d9eca2 --- /dev/null +++ b/docs/tutorials/code/getting-started-with-flask/time-app.py @@ -0,0 +1,19 @@ +import datetime + +import flask + +app = flask.Flask(__name__) + + +@app.route("/") +def index(): + return "Hello, world!\n" + + +@app.route("/time") +def time(): + return f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" + + +if __name__ == "__main__": + app.run() diff --git a/docs/tutorials/getting-started-with-flask.rst b/docs/tutorials/getting-started-with-flask.rst new file mode 100644 index 000000000..c1b4c2b51 --- /dev/null +++ b/docs/tutorials/getting-started-with-flask.rst @@ -0,0 +1,457 @@ +.. _build-a-rock-for-a-flask-application: + +Build a rock for a Flask application +------------------------------------ + +In this tutorial you will create a simple Flask application and learn how to +containerise it in a rock, using Rockcraft's ``flask-framework`` +:ref:`extension `. + +Setup +===== + +We recommend starting from a clean Ubuntu 22.04 installation. If you don't have +one available, you can create one using Multipass_: + +.. collapse:: How to create an Ubuntu 22.04 VM with Multipass + + Is Multipass_ already installed and active? Start by running + + .. code-block:: bash + + sudo snap services multipass + + If you see the ``multipass`` service but it isn't "active", then you'll + need to run ``sudo snap start multipass``. On the other hand, if you get + an error saying ``snap "multipass" not found``, then you must Install + `Multipass `_: + + .. code-block:: bash + + sudo snap install multipass + + Then you can create the VM with the following command: + + .. code-block:: text + + multipass launch --disk 10G --name flask-hello-world 22.04 + + Finally, once the VM is up, open a shell into it: + + .. code-block:: bash + + multipass shell flask-hello-world + +---- + +`LXD`_ will be required for creating the OCI image. Make sure it is installed +and initialised: + +.. code-block:: bash + + sudo snap install lxd + lxd init --auto + +In order to create the Flask rock, you'll need to install Rockcraft: + +.. code-block:: bash + + sudo snap install rockcraft --classic + +We'll use Docker run the Flask rock. You can also install it as a ``snap``: + +.. code-block:: bash + + sudo snap install docker + +.. warning:: + There is a `known connectivity issue with LXD and Docker + `_. During the tutorial, if you see a + networking issue such as "*A network related operation failed in a context + of no network access*", make sure you apply one of the fixes suggested + `here `_. + +Finally, create a new directory for this tutorial and go inside it: + +.. code-block:: bash + + mkdir flask-hello-world + cd flask-hello-world + +Note that you'll also need a text editor. You can either install one of your +choice or simply use one of the already existing editors in your Ubuntu +environment (like ``vi``). + +Create the Flask application +============================ + +Start by creating the "Hello, world" Flask application that will be used for +this tutorial. + +Create a ``requirements.txt`` file, copy the following text into it and then +save it: + +.. literalinclude:: code/getting-started-with-flask/requirements.txt + +In order to test the Flask application locally (before packing it into a rock), +install ``python3-venv`` and create a virtual environment: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:create-venv] + :end-before: [docs:create-venv-end] + :dedent: 2 + +In the same directory, copy and save the following into a text file called +``app.py``: + +.. literalinclude:: code/getting-started-with-flask/app.py + :language: python + +Run the Flask application to verify that it works: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:run-flask] + :end-before: [docs:run-flask-end] + :dedent: 2 + +.. note:: + The ``&`` at the end of the command runs the Flask application in the + background. You can continue to use your terminal as normal and will see all + the output from the Flask application in your terminal. To stop the Flask + application, you can use the ``kill`` command shown below. + +Test the Flask application by using ``curl`` to send a request to the root +endpoint: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:curl-flask] + :end-before: [docs:curl-flask-end] + :dedent: 2 + +The Flask application should respond with ``Hello, world!``. + +The Flask application looks good, so you can stop for now: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:stop-flask] + :end-before: [docs:stop-flask-end] + :dedent: 2 + +Pack the Flask application into a rock +====================================== + +First, you'll need a ``rockcraft.yaml`` file. Rockcraft will automate its +creation and tailoring for a Flask application by using the +``flask-framework`` profile: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:create-rockcraft-yaml] + :end-before: [docs:create-rockcraft-yaml-end] + :dedent: 2 + +The ``rockcraft.yaml`` file will automatically be created in your working +directory. Open it in a text editor and customise the ``name``, +``summary`` and ``description``. Ensure that ``platforms`` includes +the architecture of your host. For example, if your host uses the ARM +architecture, include ``arm64`` in ``platforms``. + +.. note:: + For this tutorial, we'll use the ``name`` "flask-hello-world" and build + the rock on an ``amd64`` platform. + +Pack the rock: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:pack] + :end-before: [docs:pack-end] + :dedent: 2 + +Once Rockcraft has finished packing the Flask rock, you'll find a new file in +your working directory (an `OCI `_ archive) with the ``.rock`` +extension: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:ls-rock] + :end-before: [docs:ls-rock-end] + :dedent: 2 + +The created rock is about 62MB in size. We will reduce its size later in this +tutorial. + +.. note:: + If you changed the ``name`` or ``version`` in ``rockcraft.yaml`` or are not + on an ``amd64`` platform, the name of the ``.rock`` file will be different + for you. + +Run the Flask rock with Docker +============================== + +You already have the rock as an `OCI `_ archive. Now you'll +need to import it into a format that Docker recognises: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:skopeo-copy] + :end-before: [docs:skopeo-copy-end] + :dedent: 2 + +Check that the image was successfully imported into Docker: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:docker-images] + :end-before: [docs:docker-images-end] + :dedent: 2 + +The output should list your Flask container image, along with its tag, ID and +size: + +.. code-block:: text + :class: log-snippets + + REPOSITORY TAG IMAGE ID CREATED SIZE + flask-hello-world 0.1 c256056698ba 2 weeks ago 149MB + +.. note:: + The size of the image reported by Docker is the uncompressed size which is + larger than the size of the compressed ``.rock`` file. + +Now you're finally ready to run the rock and test your containerised Flask +application: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:docker-run] + :end-before: [docs:docker-run-end] + :dedent: 2 + +Use the same ``curl`` command as before to send a request to the Flask +application's root endpoint which is running inside the container: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:curl-flask-rock] + :end-before: [docs:curl-flask-rock-end] + :dedent: 2 + +The Flask application should again respond with ``Hello, world!``. + +View the application logs +~~~~~~~~~~~~~~~~~~~~~~~~~ + +When deploying your Flask rock, you can always get the application logs via +``pebble``: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:get-logs] + :end-before: [docs:get-logs-end] + :dedent: 2 + +As a result, :ref:`pebble_explanation_page` will give you the logs for the +``flask`` service running inside the container. +You should expect to see something similar to this: + +.. code-block:: text + :class: log-snippets + + 2024-06-21T03:41:45.077Z [flask] [2024-06-21 03:41:45 +0000] [17] [INFO] Starting gunicorn 22.0.0 + 2024-06-21T03:41:45.077Z [flask] [2024-06-21 03:41:45 +0000] [17] [INFO] Listening at: http://0.0.0.0:8000 (17) + 2024-06-21T03:41:45.077Z [flask] [2024-06-21 03:41:45 +0000] [17] [INFO] Using worker: sync + 2024-06-21T03:41:45.078Z [flask] [2024-06-21 03:41:45 +0000] [18] [INFO] Booting worker with pid: 18 + +You can also choose to follow the logs by using the ``-f`` option with the +``pebble logs`` command above. + +.. important:: + + To get the Flask application logs, the container must be running. This is + also valid for the remaining sections of this tutorial. + +Cleanup +~~~~~~~ + +Now you have a fully functional rock for you Flask application! This concludes +the first part of this tutorial, so you can stop the container and remove the +respective image for now: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:stop-docker] + :end-before: [docs:stop-docker-end] + :dedent: 2 + +Chisel the rock +=============== + +This is an optional but recommended step, especially if you're looking to +deploy your rock into a production environment. With :ref:`chisel_explanation` +you can produce lean and production-ready rocks by getting rid of all the +contents that are not needed for your Flask application to run. This results +in a much smaller rock with a reduced attack surface. + +The first step towards chiselling your rock is to ensure you are using a +``bare`` :ref:`base `. +In ``rockcraft.yaml``, change the ``base`` to ``bare`` and add +``build-base: ubuntu@22.04``: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:change-base] + :end-before: [docs:change-base-end] + :dedent: 2 + +Pack the rock with the new ``bare`` :ref:`base `: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:chisel-pack] + :end-before: [docs:chisel-pack-end] + :dedent: 2 + +As before, verify that the new rock was created: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:ls-bare-rock] + :end-before: [docs:ls-bare-rock-end] + :dedent: 2 + +You'll verify that the new Flask rock is now approximately **15% smaller** +in size! And that's just because of the simple change of ``base``. + +And the functionality is still the same. As before, you can confirm this by +running the rock with Docker + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:docker-run-chisel] + :end-before: [docs:docker-run-chisel-end] + :dedent: 2 + +and then using the same ``curl`` request: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:curl-flask-bare-rock] + :end-before: [docs:curl-flask-bare-rock-end] + :dedent: 2 + +Unsurprisingly, the Flask application should still respond with +``Hello, world!``. + +Cleanup +~~~~~~~ + +And that's it. You can now stop container and remove the corresponding image: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:stop-docker-chisel] + :end-before: [docs:stop-docker-chisel-end] + :dedent: 2 + +.. _update-flask-application: + +Update Flask application +======================== + +As a final step, let's say you want to update your application. For example, +you want to add a new ``/time`` endpoint which returns the current time. + +Start by opening the ``app.py`` file in a text editor and update the code to +look like the following: + +.. literalinclude:: code/getting-started-with-flask/time-app.py + :language: python + +Since you are creating a new version of your application, open the +``rockfile.yaml`` file and change the ``version`` (e.g. to ``0.2``). + +Pack and run the rock using similar commands as before: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:docker-run-update] + :end-before: [docs:docker-run-update-end] + :dedent: 2 + +.. note:: + + Note that the resulting ``.rock`` file will now be named differently, as + its new version will be part of the filename. + +Finally, use ``curl`` to send a request to the ``/time`` endpoint: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: text + :start-after: [docs:curl-time] + :end-before: [docs:curl-time-end] + :dedent: 2 + +The updated application should respond with the current date and time (e.g. +``2024-06-21 09:47:56``). + +Cleanup +~~~~~~~ + +You can now stop container and remove the corresponding image: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:stop-docker-updated] + :end-before: [docs:stop-docker-updated-end] + :dedent: 2 + +Reset your environment +====================== + +You've reached the end of this tutorial. + +If you'd like to reset your working environment, you can simply run the +following: + +.. literalinclude:: code/getting-started-with-flask/task.yaml + :language: bash + :start-after: [docs:cleanup] + :end-before: [docs:cleanup-end] + :dedent: 2 + +.. collapse:: If using Multipass... + + If you created an instance using Multipass, you can also clean it up. + Start by exiting it: + + .. code-block:: bash + + exit + + And then you can proceed with its deletion: + + .. code-block:: bash + + multipass delete flask-hello-world + multipass purge + +---- + +Troubleshooting +=============== + +**Application updates not taking effect?** + +Upon changing your Flask application and re-packing the rock, if you believe +your changes are not taking effect (e.g. the ``/time`` +:ref:`endpoint ` is returning a +404), try running ``rockcraft clean`` and pack the rock again with +``rockcraft pack``. + +.. _`lxd-docker-connectivity-issue`: https://documentation.ubuntu.com/lxd/en/latest/howto/network_bridge_firewalld/#prevent-connectivity-issues-with-lxd-and-docker +.. _`install-multipass`: https://multipass.run/docs/how-to-install-multipass diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst index 3ef9b71b0..44cf41888 100644 --- a/docs/tutorials/index.rst +++ b/docs/tutorials/index.rst @@ -24,3 +24,4 @@ code into container applications: 3. Containerise a PyPI package 4. Bundle a Node.js app within a rock + 5. Build a rock for a Flask application diff --git a/pyproject.toml b/pyproject.toml index 3a18e0daa..aab5f5670 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ extend-exclude = "docs/sphinx-starter-pack" ignore = [ "docs/sphinx-starter-pack", "docs/how-to/code", + "docs/tutorials/code/getting-started-with-flask", "tests/spread/general/extension-django/example_django" ]