From 529446b2fc6e597e09873e60d98113f1cb32603e Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Tue, 17 Dec 2024 12:40:02 -0500 Subject: [PATCH] docs: add "How to manage 12-factor app charms" I'm breaking down the content from PR #2010 into smaller chunks. This is "How to manage 12-factor app charms" converted to RST: https://github.com/canonical/charmcraft/blob/737976c943a601adccd883ffa6a276bce625c9a1/docs/howto/manage-12-factor-app-charms.md The main differences from the Markdown version: - Uses tabs instead of dropdowns (matching the Rockcraft docs: https://documentation.ubuntu.com/rockcraft/en/latest/how-to/build-a-12-factor-app-rock/) - "See also" blocks are added as cards --- common.mk | 2 +- docs/howto/index.rst | 1 + docs/howto/manage-12-factor-app-charms.rst | 342 +++++++++++++++++++++ 3 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 docs/howto/manage-12-factor-app-charms.rst diff --git a/common.mk b/common.mk index 4f5c9ef49..fafb941cc 100644 --- a/common.mk +++ b/common.mk @@ -194,7 +194,7 @@ docs: ## Build documentation .PHONY: docs-auto docs-auto: ## Build and host docs with sphinx-autobuild - uv run --extra docs sphinx-autobuild -b html --open-browser --port=8080 --ignore *.kate-swp --ignore docs/reference/commands/** --watch $(PROJECT) $(DOCS) $(DOCS)/_build + uv run --extra docs sphinx-autobuild -b html --open-browser --port=8080 --ignore *.kate-swp --ignore 'docs/reference/commands/**' --watch $(PROJECT) $(DOCS) $(DOCS)/_build .PHONY: pack-pip pack-pip: ##- Build packages for pip (sdist, wheel) diff --git a/docs/howto/index.rst b/docs/howto/index.rst index 9bf000898..0eeeb098a 100644 --- a/docs/howto/index.rst +++ b/docs/howto/index.rst @@ -6,6 +6,7 @@ How-To .. toctree:: :maxdepth: 2 + manage-12-factor-app-charms charm-to-poetry charm-to-python shared-cache diff --git a/docs/howto/manage-12-factor-app-charms.rst b/docs/howto/manage-12-factor-app-charms.rst new file mode 100644 index 000000000..4c7b770ad --- /dev/null +++ b/docs/howto/manage-12-factor-app-charms.rst @@ -0,0 +1,342 @@ +.. _howto-manage-12-factor-app-charms + +Manage 12-factor app charms +*************************** + +.. raw:: html + + + +.. card:: See also + :link: https://juju.is/docs/juju/charmed-operator + + ``juju`` | 12-factor app charms + +Prepare an OCI image for a 12-factor app charm +---------------------------------------------- + +.. card:: See more + :link: https://documentation.ubuntu.com/rockcraft/en/latest/how-to/build-a-12-factor-app-rock + + ``rockcraft`` | How to build a 12-factor app + +Initialise a 12-factor app charm +-------------------------------- + +Use ``charmcraft init`` and specify the relevant profile: + +.. tabs:: + :sync-group: framework + + .. group-tab:: Flask + + .. code:: text + + charmcraft init --profile flask-framework + + .. group-tab:: Django + + .. code:: text + + charmcraft init --profile django-framework + + .. group-tab:: FastAPI + + .. code:: text + + charmcraft init --profile fastapi-framework + + .. group-tab:: Go + + .. code:: text + + charmcraft init --profile go-framework + +Charmcraft automatically creates ``charmcraft.yaml``, +``requirements.txt`` and source code for the charm in your current +directory. You will need to check ``charmcraft.yaml`` and ``README.md`` +and verify that the charm’s name and description are correct. + +.. card:: See also: + :link-type: ref + :link: ref_commands_init + + ``init`` command + + +Manage configurations for a 12-factor app charm +----------------------------------------------- + +A charm configuration can be added if your 12-factor app requires +environment variables, for example, to pass a token for a service. Add +the configuration in ``charmcraft.yaml``: + +.. code:: text + + config: + options: + token: + description: The token for the service. + type: string + +.. tabs:: + :sync-group: framework + + .. group-tab:: Flask + + A user-defined configuration option will correspond to an environment + variable generated by the ``paas-charm`` project to expose the + configuration to the Flask workload. In general, a configuration option + ``config-option-name`` will be mapped as ``FLASK_CONFIG_OPTION_NAME`` + where the option name will be converted to upper case, dashes will be + converted to underscores and the ``FLASK_`` prefix will be added. In the + example above, the ``token`` configuration will be mapped as the + ``FLASK_TOKEN`` environment variable. In addition to the environment + variable, the configuration is also available in the Flask variable + ``app.config`` without the ``FLASK_`` prefix. + + The configuration can be set on the deployed charm using + ``juju config token=``. + + .. card-carousel:: 2 + + .. card:: See also + :link: https://juju.is/docs/sdk/config + + How to add configuration to a charm + + .. card:: See also + :link: https://flask.palletsprojects.com/en/3.0.x/config/ + + Configuration Handling – Flask Documentation + + .. group-tab:: Django + + A user-defined configuration option will correspond to an environment variable generated by the ``paas-charm`` project to expose the configuration to the Django workload. In general, a configuration option ``config-option-name`` will be mapped as ``DJANGO_CONFIG_OPTION_NAME`` where the option name will be converted to upper case, dashes will be converted to underscores and the ``DJANGO_`` prefix will be added. In the example above, the ``token`` configuration will be mapped as the ``DJANGO_TOKEN`` environment variable. + + The configuration can be set on the deployed charm using ``juju config token=``. + + .. card-carousel:: 2 + + .. card:: See also + :link: https://juju.is/docs/sdk/config + + How to add configuration to a charm + + .. card:: See also + :link: https://docs.djangoproject.com/en/stable/topics/settings/ + + Django settings | Django documentation + + .. group-tab:: FastAPI + + A user-defined configuration option will correspond to an environment + variable generated by the ``paas-charm`` project to expose the + configuration to the FastAPI workload. In general, a configuration + option ``config-option-name`` will be mapped as + ``APP_CONFIG_OPTION_NAME`` where the option name will be converted to + upper case, dashes will be converted to underscores and the ``APP_`` + prefix will be added. In the example above, the ``token`` configuration + will be mapped as the ``APP_TOKEN`` environment variable. + + The configuration can be set on the deployed charm using + ``juju config token=``. + + .. card-carousel:: 2 + + .. card:: See also + :link: https://juju.is/docs/sdk/config + + How to add configuration to a charm + + .. card:: See also + :link: https://fastapi.tiangolo.com/advanced/settings/ + + Settings and Environment Variables - FastAPI + + .. group-tab:: Go + + A user-defined configuration option will correspond to an environment variable generated by the ``paas-charm`` project to expose the configuration to the Go workload. In general, a configuration option ``config-option-name`` will be mapped as ``APP_CONFIG_OPTION_NAME`` where the option name will be converted to upper case, dashes will be converted to underscores and the ``APP_`` prefix will be added. In the example above, the `token`` configuration will be mapped as the ``APP_TOKEN`` environment variable. + + The configuration can be set on the deployed charm using ``juju config token=``. + + .. card-carousel:: 2 + + .. card:: See also + :link: https://juju.is/docs/sdk/config + + How to add configuration to a charm + + +Manage relations for a 12-factor app charm +------------------------------------------ + +A charm integration can be added to your charmed 12-factor app by +providing the integration and endpoint definition in +``charmcraft.yaml``: + +.. code:: yaml + + requires: + : + interface: + optional: false + +Here, ```` corresponds to the endpoint of the application +with which you want the integration, and ```` +is the endpoint schema to which this relation conforms. Both the +```` and ```` must coincide with +the structs defined in that particular application’s charm’s +``charmcraft.yaml`` file. The key ``optional`` with value ``False`` +means that the charm will get blocked and stop the services if the +integration is not provided. + +You can provide the integration to your deployed 12-factor app using +``juju integrate <12-factor app charm> ``. After the +integration has been established, the connection string and other +configuration options will be available as environment variables that +you may use to configure your 12-factor application. + +For example, if you wish to integrate your 12-factor application with +PostgreSQL (`machine `__ or +`k8s `__ charm), add the following +endpoint definition to ``charmcraft.yaml``: + +.. code:: yaml + + requires: + postgresql: + interface: postgresql_client + optional: True + +Provide the integration to your deployed 12-factor app with +``juju integrate <12-factor app charm> postgresql``. This integration +creates the following environment variables you may use to configure +your 12-factor application: + +- ``POSTGRESQL_DB_CONNECT_STRING`` +- ``POSTGRESQL_DB_SCHEME`` +- ``POSTGRESQL_DB_NETLOC`` +- ``POSTGRESQL_DB_PATH`` +- ``POSTGRESQL_DB_PARAMS`` +- ``POSTGRESQL_DB_QUERY`` +- ``POSTGRESQL_DB_FRAGMENT`` +- ``POSTGRESQL_DB_USERNAME`` +- ``POSTGRESQL_DB_PASSWORD`` +- ``POSTGRESQL_DB_HOSTNAME`` +- ``POSTGRESQL_DB_PORT`` + +.. card:: See also + :link: https://juju.is/docs/sdk/implement-integrations-in-a-charm + + How to add an integration to a charm + +Manage secrets for a 12-factor app charm +---------------------------------------- + +A user secret can be added to a charm and all the keys and values in the +secret will be exposed as environment variables. Add the secret +configuration option in charmcraft.yaml: + +.. code:: yaml + + config: + options: + api-token: + type: secret + description: Secret needed to access some API secret information + +Once the charm is deployed, you can add a Juju secret to the model:: + + juju add-secret my-api-token value=1234 othervalue=5678 + +The output from the previous command will look something like:: + + secret:cru00lvmp25c77qa0qrg + +From the output of the previous command, you can get the Juju secret ID. +Grant the application access to view the value of the secret:: + + juju grant-secret my-api-token + +Add the Juju secret ID to the application:: + + juju config api-token=secret:cru00lvmp25c77qa0qrg + +.. tabs:: + :sync-group: framework + + .. group-tab:: Flask + + The following environment variables are available for the application: + + - ``FLASK_API_TOKEN_VALUE``: ``"1234"`` + - ``FLASK_API_TOKEN_OTHERVALUE``: ``"5678"`` + + .. group-tab:: Django + + The following environment variables are available for the application: + + - ``DJANGO_API_TOKEN_VALUE``: ``"1234"`` + - ``DJANGO_API_TOKEN_OTHERVALUE``: ``"5678"`` + + .. group-tab:: FastAPI + + The following environment variables are available for the application: + + - ``APP_API_TOKEN_VALUE``: ``"1234"`` + - ``APP_API_TOKEN_OTHERVALUE``: ``"5678"`` + + .. group-tab:: Go + + The following environment variables are available for the application: + + - ``APP_API_TOKEN_VALUE``: ``"1234"`` + - ``APP_API_TOKEN_OTHERVALUE``: ``"5678"`` + +.. card:: See also + :link: https://juju.is/docs/sdk/add-a-secret-to-a-charm + + How to manage secrets + +Use 12-factor app charms +------------------------ + +.. tabs:: + :sync-group: framework + + .. group-tab:: Flask + + .. group-tab:: Django + + Use the ``create-superuser`` action to create a new Django admin account:: + + juju run create-superuser username= email= + + You must provide the username and email address. + + .. group-tab:: FastAPI + + .. group-tab:: Go + +(If your workload depends on a database) Migrate the database +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + If your app depends on a database, it is common to run a database migration script + before app startup which, for example, creates or modifies tables. This can be done + by including the ``migrate.sh`` script in the root of your project. It will be + executed with the same environment variables and context as the 12-factor app. + + If the migration script fails, it will retry upon ``update-status``. The migration + script will run on every unit. The script is assumed to be idempotent (i.e., can + be run multiple times) and that it can be run on multiple units simultaneously + without issue. Handling multiple migration scripts that run concurrently can be + achieved by, for example, locking any tables during the migration.