From ed318e08cc0eb89072b2106e4b541107b155e40a Mon Sep 17 00:00:00 2001 From: Teodora Mihoc Date: Tue, 26 Nov 2024 17:32:40 +0100 Subject: [PATCH 01/19] docs: add, refactor, and reformat discourse docs feat. charmcraft --- docs/explanation/#index.rst# | 18 + docs/explanation/index.rst | 14 +- docs/howto/index.md | 23 + docs/howto/index.rst | 11 - docs/howto/manage-12-factor-app-charms.md | 259 ++++ docs/howto/manage-bundles.md | 40 + docs/howto/manage-channels.md | 75 + docs/howto/manage-charms.md | 507 ++++++ docs/howto/manage-extensions.md | 209 +++ docs/howto/manage-icons-create-1.png | Bin 0 -> 157228 bytes docs/howto/manage-icons-create-2.png | Bin 0 -> 199852 bytes docs/howto/manage-icons-create-3.png | Bin 0 -> 200834 bytes docs/howto/manage-icons-validate.png | Bin 0 -> 28958 bytes docs/howto/manage-icons.md | 70 + docs/howto/manage-libraries.md | 111 ++ docs/howto/manage-names.md | 36 + docs/howto/manage-parts.md | 48 + docs/howto/manage-resources.md | 97 ++ docs/howto/manage-revisions.md | 81 + docs/howto/manage-the-charmcraft-cli.md | 119 ++ .../howto/manage-the-current-charmhub-user.md | 99 ++ docs/howto/manage-tracks.md | 101 ++ .../#shared-cache.rst#} | 0 docs/howto/{ => misc}/charm-to-poetry.rst | 0 docs/howto/{ => misc}/charm-to-python.rst | 0 docs/howto/misc/index.md | 16 + ...ack-a-hooks-based-charm-with-charmcraft.md | 158 ++ ...-a-reactive-based-charm-with-charmcraft.md | 126 ++ docs/howto/misc/shared-cache.rst | 78 + docs/index.md | 81 + docs/index.rst | 54 - .../charmcraft-analyzers-and-linters.md | 88 ++ docs/reference/charmcraft-cli.md | 7 + docs/reference/charmcraft.md | 7 + docs/reference/extension.md | 15 + .../charmcraft-extension-django-framework.md | 225 +++ .../charmcraft-extension-fastapi-framework.md | 209 +++ .../charmcraft-extension-flask-framework.md | 237 +++ .../charmcraft-extension-go-framework.md | 229 +++ docs/reference/extensions/index.md | 15 + docs/reference/file.md | 11 + docs/reference/files/file-actions-yaml.md | 236 +++ docs/reference/files/file-bundle-yaml.md | 870 +++++++++++ docs/reference/files/file-charmcraft-yaml.md | 1356 +++++++++++++++++ docs/reference/files/file-config-yaml.md | 80 + docs/reference/files/file-contributing-md.md | 6 + docs/reference/files/file-dispatch.md | 18 + docs/reference/files/file-icon-svg.md | 24 + docs/reference/files/file-libname-py.md | 160 ++ docs/reference/files/file-license.md | 8 + docs/reference/files/file-lxd-profile-yaml.md | 60 + docs/reference/files/file-manifest-yaml.md | 10 + docs/reference/files/file-metadata-yaml.md | 900 +++++++++++ docs/reference/files/file-pyproject-toml.md | 56 + docs/reference/files/file-readme-md.md | 7 + .../files/file-requirements-dev-txt.md | 6 + docs/reference/files/file-requirements-txt.md | 8 + docs/reference/files/file-src-charm-py.md | 7 + .../file-tests-integration-test-charm-py.md | 48 + .../files/file-tests-unit-test-charm-py.md | 37 + docs/reference/files/file-tox-ini.md | 97 ++ docs/reference/files/index.md | 30 + docs/reference/index.rst | 17 +- docs/{explanation => reference}/lifecycle.rst | 1 + ...st => list-of-charmcraft-cli-commands.rst} | 5 +- docs/reference/{parts.rst => part.rst} | 12 +- docs/reference/plugins/index.rst | 5 +- docs/reference/profile.md | 8 + docs/tutorial/index.md | 19 + ...first-kubernetes-charm-for-a-django-app.md | 567 +++++++ ...irst-kubernetes-charm-for-a-fastapi-app.md | 549 +++++++ ...our-first-kubernetes-charm-for-a-go-app.md | 620 ++++++++ 72 files changed, 9207 insertions(+), 94 deletions(-) create mode 100644 docs/explanation/#index.rst# create mode 100644 docs/howto/index.md delete mode 100644 docs/howto/index.rst create mode 100644 docs/howto/manage-12-factor-app-charms.md create mode 100644 docs/howto/manage-bundles.md create mode 100644 docs/howto/manage-channels.md create mode 100644 docs/howto/manage-charms.md create mode 100644 docs/howto/manage-extensions.md create mode 100644 docs/howto/manage-icons-create-1.png create mode 100644 docs/howto/manage-icons-create-2.png create mode 100644 docs/howto/manage-icons-create-3.png create mode 100644 docs/howto/manage-icons-validate.png create mode 100644 docs/howto/manage-icons.md create mode 100644 docs/howto/manage-libraries.md create mode 100644 docs/howto/manage-names.md create mode 100644 docs/howto/manage-parts.md create mode 100644 docs/howto/manage-resources.md create mode 100644 docs/howto/manage-revisions.md create mode 100644 docs/howto/manage-the-charmcraft-cli.md create mode 100644 docs/howto/manage-the-current-charmhub-user.md create mode 100644 docs/howto/manage-tracks.md rename docs/howto/{shared-cache.rst => misc/#shared-cache.rst#} (100%) rename docs/howto/{ => misc}/charm-to-poetry.rst (100%) rename docs/howto/{ => misc}/charm-to-python.rst (100%) create mode 100644 docs/howto/misc/index.md create mode 100644 docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md create mode 100644 docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md create mode 100644 docs/howto/misc/shared-cache.rst create mode 100644 docs/index.md delete mode 100644 docs/index.rst create mode 100644 docs/reference/charmcraft-analyzers-and-linters.md create mode 100644 docs/reference/charmcraft-cli.md create mode 100644 docs/reference/charmcraft.md create mode 100644 docs/reference/extension.md create mode 100644 docs/reference/extensions/charmcraft-extension-django-framework.md create mode 100644 docs/reference/extensions/charmcraft-extension-fastapi-framework.md create mode 100644 docs/reference/extensions/charmcraft-extension-flask-framework.md create mode 100644 docs/reference/extensions/charmcraft-extension-go-framework.md create mode 100644 docs/reference/extensions/index.md create mode 100644 docs/reference/file.md create mode 100644 docs/reference/files/file-actions-yaml.md create mode 100644 docs/reference/files/file-bundle-yaml.md create mode 100644 docs/reference/files/file-charmcraft-yaml.md create mode 100644 docs/reference/files/file-config-yaml.md create mode 100644 docs/reference/files/file-contributing-md.md create mode 100644 docs/reference/files/file-dispatch.md create mode 100644 docs/reference/files/file-icon-svg.md create mode 100644 docs/reference/files/file-libname-py.md create mode 100644 docs/reference/files/file-license.md create mode 100644 docs/reference/files/file-lxd-profile-yaml.md create mode 100644 docs/reference/files/file-manifest-yaml.md create mode 100644 docs/reference/files/file-metadata-yaml.md create mode 100644 docs/reference/files/file-pyproject-toml.md create mode 100644 docs/reference/files/file-readme-md.md create mode 100644 docs/reference/files/file-requirements-dev-txt.md create mode 100644 docs/reference/files/file-requirements-txt.md create mode 100644 docs/reference/files/file-src-charm-py.md create mode 100644 docs/reference/files/file-tests-integration-test-charm-py.md create mode 100644 docs/reference/files/file-tests-unit-test-charm-py.md create mode 100644 docs/reference/files/file-tox-ini.md create mode 100644 docs/reference/files/index.md rename docs/{explanation => reference}/lifecycle.rst (99%) rename docs/reference/{commands.rst => list-of-charmcraft-cli-commands.rst} (92%) rename docs/reference/{parts.rst => part.rst} (75%) create mode 100644 docs/reference/profile.md create mode 100644 docs/tutorial/index.md create mode 100644 docs/tutorial/write-your-first-kubernetes-charm-for-a-django-app.md create mode 100644 docs/tutorial/write-your-first-kubernetes-charm-for-a-fastapi-app.md create mode 100644 docs/tutorial/write-your-first-kubernetes-charm-for-a-go-app.md diff --git a/docs/explanation/#index.rst# b/docs/explanation/#index.rst# new file mode 100644 index 000000000..d590cad37 --- /dev/null +++ b/docs/explanation/#index.rst# @@ -0,0 +1,18 @@ +.. _explanation: + +Explanation +*********** + +As Charmcraft is a part of the `Juju Charm SDK `_, most +explanation is hosted on the `Charm SDK docs `_. + +.. toctree:: + :maxdepth: 1 + + /common/craft-parts/explanation/dump_plugin + /common/craft-parts/explanation/filesets + lifecycle + + +`Charm SDK docs `_ +======================================================== diff --git a/docs/explanation/index.rst b/docs/explanation/index.rst index d590cad37..66f1ee64f 100644 --- a/docs/explanation/index.rst +++ b/docs/explanation/index.rst @@ -3,16 +3,4 @@ Explanation *********** -As Charmcraft is a part of the `Juju Charm SDK `_, most -explanation is hosted on the `Charm SDK docs `_. - -.. toctree:: - :maxdepth: 1 - - /common/craft-parts/explanation/dump_plugin - /common/craft-parts/explanation/filesets - lifecycle - - -`Charm SDK docs `_ -======================================================== +TBA diff --git a/docs/howto/index.md b/docs/howto/index.md new file mode 100644 index 000000000..f4e977f3c --- /dev/null +++ b/docs/howto/index.md @@ -0,0 +1,23 @@ +(how-to-guides)= +# How-to guides + + +```{toctree} +:maxdepth: 2 + +Manage the `charmcraft` CLI +Manage charms +Manage charms (12-factor apps) +Manage extensions +Manage resources +Manage libraries +Manage parts +Manage the current Charmhub user +Manage names +Manage revisions +Manage channels +Manage tracks +Manage icons +Misc +Manage bundles +``` diff --git a/docs/howto/index.rst b/docs/howto/index.rst deleted file mode 100644 index 9bf000898..000000000 --- a/docs/howto/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _howto: - -How-To -****** - -.. toctree:: - :maxdepth: 2 - - charm-to-poetry - charm-to-python - shared-cache diff --git a/docs/howto/manage-12-factor-app-charms.md b/docs/howto/manage-12-factor-app-charms.md new file mode 100644 index 000000000..5bd612b57 --- /dev/null +++ b/docs/howto/manage-12-factor-app-charms.md @@ -0,0 +1,259 @@ +(manage-12-factor-app-charms)= +# How to manage 12-factor app charms + + + + +> See also: [`juju` | 12-factor-app charms](https://juju.is/docs/juju/charmed-operator) + +## Prepare an OCI image for a 12-factor app charm + +> See more: [`rockcraft` | Build a 12-factor app rock](https://documentation.ubuntu.com/rockcraft/en/latest/how-to/build-a-12-factor-app-rock/#include-extra-files-in-the-oci-image) + + +## Initialise a 12-factor app charm + +Use `charmcraft init` and specify the relevant profile: + +```text +charmcraft init --profile +``` + +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. + +> See also: {ref}`ref_commands_init` + +````{dropdown} Example: Flask application + +Specify the `flask-framework` profile: + +```text +charmcraft init --profile flask-framework +``` + +```` + +````{dropdown} Example: Django application + +Specify the `django-framework` profile: + +```text +charmcraft init --profile django-framework +``` + +```` + +````{dropdown} Example: FastAPI application + + +Specify the `fastapi-framework` profile: + +```text +charmcraft init --profile fastapi-framework +``` + +```` + +````{dropdown} Example: Go application + +Specify the `go-framework` profile: + +```text +charmcraft init --profile go-framework +``` + +```` + +## 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`: + +```text +config: + options: + token: + description: The token for the service. + type: string +``` + +```{dropdown} Flask application + +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=`. + +> See also: [How to add a configuration to a charm `, [Configuration Handling – Flask Documentation^](https://flask.palletsprojects.com/en/3.0.x/config/) + +``` + +```{dropdown} Django application +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=`. + +> See also: [How to add a configuration to a charm ` +``` + +```{dropdown} FastAPI application + +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=`. + +> See also: [How to add a configuration to a charm ` +{ref}`/tab] + +``` + + + +```{dropdown} Go application +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=`. + +> See also: [How to add a 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`: + +```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](https://charmhub.io/postgresql) or [k8s](https://charmhub.io/postgresql-k8s) charm), add the following endpoint definition to `charmcraft.yaml`: + +```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` + +> See also: {ref}`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: +```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 +``` + +```{dropdown} Flask application +The following environment variables are available for the application: + + - FLASK_API_TOKEN_VALUE: "1234" + - FLASK_API_TOKEN_OTHERVALUE: "5678" + +> See also: [How to manage secrets ` +``` + +```{dropdown} Django application +The following environment variables are available for the application: + - DJANGO_API_TOKEN_VALUE: "1234" + - DJANGO_API_TOKEN_OTHERVALUE: "5678" + +> See also: [How to manage secrets ` +``` + +```{dropdown} FastAPI application +The following environment variables are available for the application: + - APP_API_TOKEN_VALUE: "1234" + - APP_API_TOKEN_OTHERVALUE: "5678" + +> See also: [How to manage secrets ` +``` + +```{dropdown} Go application +The following environment variables are available for the application: + - APP_API_TOKEN_VALUE: "1234" + - APP_API_TOKEN_OTHERVALUE: "5678" + +> See also: [How to manage secrets ` +``` + +## Use 12-factor app charms + +### (If your charm is a Django charm) Create an admin user + +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. + +### (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. + + diff --git a/docs/howto/manage-bundles.md b/docs/howto/manage-bundles.md new file mode 100644 index 000000000..cacceafcd --- /dev/null +++ b/docs/howto/manage-bundles.md @@ -0,0 +1,40 @@ +(how-to-manage-charm-bundles)= +# How to manage charm bundles + +> See first: [`juju` | Bundle](https://juju.is/docs/juju/bundle) + +```{important} +Starting with 1 Jan 2025, bundles are being phased out. + +``` + +## Create a bundle + +To create a bundle, create a `.yaml` file with your desired configuration. + +```{tip} + +If you don't want to start from scratch, export the contents of your model to a `.yaml` file via `juju export-bundle --filename .yaml` or download the `.yaml` of an existing bundle from Charmhub. + +> See more: [Juju | How to compare and export the contents of a model to a bundle](https://juju.is/docs/juju/manage-models#heading--compare-and-export-the-contents-of-a-model-to-a-bundle) + + +``` + +> See more: {ref}`file-bundle-yaml` + +## Pack a bundle + +To pack a bundle, in the directory where you have your `bundle.yaml` file (and possibly other files, e.g., a `README.md` file), create a `charmcraft.yaml` file suitable for a bundle (at the minimum: `type: bundle`), then run `charmcraft pack` to pack the bundle. The result is a `.zip` file. + +> See more: {ref}`ref_commands_pack` + + +## Publish a bundle on Charmhub + +The process is identical to that for a simple charm except that, at the step where you register the name, for bundles the command is `register-bundle`. + + +> See more: {ref}`publish-a-charm` + + diff --git a/docs/howto/manage-channels.md b/docs/howto/manage-channels.md new file mode 100644 index 000000000..10c132c27 --- /dev/null +++ b/docs/howto/manage-channels.md @@ -0,0 +1,75 @@ +(manage-channels)= +# How to manage channels + + +## Create a channel + +When you register a name on Charmhub, that automatically creates 4 channels, all with track `latest` but with different risk levels, namely, `edge`, `beta`, `candidate`, `stable`, respectively. + +> See more: {ref}`register-a-name` + + + +## View the available channels + +To view a charm's channels on Charmhub, run `charmcraft status` followed by the name of the charm. E.g., + +```bash +$ charmcraft status my-awesome-charm +Track Channel Version Revision +latest stable - - + candidate - - + beta 0.1 1 + edge ↑ ↑ +``` + +(The above output shows 4 channels, all of which have the same track, `latest`, but different risk levels, namely, `edge`, `beta`, `candidate`, and `stable`.) + +> See more: {ref}`command-charmcraft-status` + + +## Customise a channel's track + + +Request a track guardrail and create a track + +See {ref}`manage-tracks`. + + +## Open a channel + + +A channel is opened implicitly when you release a revision to it. + + + +## Close a channel + +A channel is opened when you release a revision to that channel. Before that, the channel is created but not opened. When you're closing a channel, e.g., latest/candidate, that means that any deployment requests that go there will be forwarded to the next most stable risk, e.g., for beta, latest/stable. If you close stable, you can no longer deploy or update from that, unless you release again to that channel (because releasing opens the channel). + +If you add a branch, closing that branch will forward people to the same track and risk, without a branch. diff --git a/docs/howto/manage-charms.md b/docs/howto/manage-charms.md new file mode 100644 index 000000000..09a7f8ccb --- /dev/null +++ b/docs/howto/manage-charms.md @@ -0,0 +1,507 @@ +(manage-charms)= +# How to manage charms + +> See first: [`juju` | Charm](https://juju.is/docs/juju/charmed-operator), [`juju` | Manage charms](https://juju.is/docs/juju/manage-charms-or-bundles) + + +## Initialise a charm + +To initialise a charm project, create a directory for your charm, enter it, then run `charmcraft init` with the `--profile` flag followed by a suitable profile name (for machine charms: `machine`; for Kubernetes charms: `kubernetes`, `simple`, or `flask-framework`); that will create all the necessary files and even prepopulate them with useful content. + +```text +charmcraft init --profile +``` + + +````{dropdown} See sample session + + +```bash +$ mkdir my-flask-app-k8s +$ cd my-flask-app-k8s/ +$ charmcraft init --profile flask-framework +Charmed operator package file and directory tree initialised. + +Now edit the following package files to provide fundamental charm metadata +and other information: + +charmcraft.yaml +src/charm.py +README.md + +$ ls -R +.: +charmcraft.yaml requirements.txt src + +./src: +charm.py + + +``` + +```` + + + +The command also allows you to not specify any profile (in that case you get the `simple` profile -- a Kubernetes profile with lots of scaffolding, suitable for beginners) and has flags that you can use to specify a different directory to operate in, a charm name different from the name of the root directory, etc. + +> See more: {ref}`ref_commands_revisions`, {ref}`profile`, {ref}`list-of-files-in-a-charm-project` +> +> See more: {ref}`manage-extensions` + +## Add charm project metadata, an icon, docs + + +### Specify that the project is a charm + +To specify that the project is a charm (as supposed to a bundle), in your `charmcraft.yaml` file set the `type` key to `charm`: + +```text +type: charm +``` + +### Specify a name + +To specify a pack-and-deploy name for your charm, in your charm's `charmcraft.yaml` file specify the `name` key. E.g., + +```yaml +name: traefik-k8s +``` + +> See more: {ref}`file-charmcraft-yaml-name` + +### Specify a title + +To specify a title for your charm's page on Charmhub, in your charm's `charmcraft.yaml` file specify a value for the `title` key. E.g., + +```yaml +title: | + Traefik Ingress Operator for Kubernetes +``` + +> See more: {ref}`file-charmcraft-yaml-title` + +### Add a summary + +To add a summary line for your charm, in your charm's `charmcraft.yaml` file specify a value for the `summary` key. E.g., + +```yaml +summary: | + A Juju charm to run a Traefik-powered ingress controller on Kubernetes. +``` + +> See more: {ref}`file-charmcraft-yaml-summary` + +### Add a description + +To add a longer description for your charm, in your charm's `charmcraft.yaml` file specify a value for the `description` key. E.g., + +```yaml +description: | + A Juju-operated Traefik operator that routes requests from the outside of a + Kubernetes cluster to Juju units and applications. + +``` + +> See more: {ref}`file-charmcraft-yaml-description` + +### Add contact information + +To add maintainer contact information for a charm, in your charm's `charmcraft.yaml` file specify a value for the `links.contact` key. E.g., + +```yaml +links: + contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont +``` + +> See more: {ref}`file-charmcraft-yaml-contact` + +### Add a link to source code + +To add a link to the source code for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.source` key. E.g., + +```yaml +links: + source: + - https://github.com/canonical/traefik-k8s-operator +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add a link to the bug tracker + +To add a link to the bug tracker for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.issues` key. E.g., + +```yaml +links: + issues: + - https://github.com/canonical/traefik-k8s-operator/issues +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add a link to the website + +If your charm has a website outside of Charmhub, to add a link to this website, in your charm's `charmcraft.yaml` file specify an item under the `links.website` key. E.g., + +```yaml +links: + website: + - https://charmed-kubeflow.io/ +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add docs and a link to the docs + +If you publish your charm on Charmhub, reference documentation about the charm's resources, actions, configurations, relations, and libraries is extracted automatically. However, you should also aim to add further docs, e.g., a tutorial, how-to guides, etc. To provide a link to these docs, in your charm's `charmcraft.yaml` file specify a value for the `links.documentation` key. Note that at present this must be a Discourse page. E.g., + +```yaml +links: + documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add terms of use + +To add terms of use for your charm, in your charm's `charmcraft.yaml` file specify a value for the `terms` key. E.g., + +```yaml +terms: +- Butterscotch is regal +- Cara is adorable +``` + +> See more: {ref}`file-charmcraft-yaml-terms` + + +### Add an icon + +See {ref}`manage-icons`. + + +## Add runtime details to a charm + +### Require a specific Juju version + +To require a specific Juju version for your charm, in your charm's `charmcraft.yaml` specify the `assumes` key. E.g., + +```yaml +assumes: + - juju >= 3.5 +``` + +> See more: {ref}`file-charmcraft-yaml-assumes` + +### Require a Kubernetes cloud + +To require a Kubernetes cloud for your charm, in your charm's `charmcraft.yaml` file specify the `assumes` key. E.g., + +```yaml +assumes: + - k8s-api +``` + +> See more: {ref}`file-charmcraft-yaml-assumes` + +### Require a specific base and platforms + +To require a specific base and platforms for your charm, in your charm's `charmcraft.yaml` file specify the `base`(,`build-base`,) and the `platforms keys. E.g., + +```{note} +In Charmcraft < 3.0 this was done via a single key: `bases`. + +``` + +```yaml +# The run time base, the base format is @, +# accepted bases are: +# - ubuntu@24.04 +base: +# The build time base, if not defined the base is also the build time +# base, in addition to valid bases, the build-base can be "devel" +# which would use the latest in development Ubuntu Series. +build-base: + +platforms: + # The supported platforms, may omit build-for if platform-name + # is a valid arch, valid architectures follow the Debian architecture names, + # accepted architectures are: + # - amd64 + # - arm64 + # - armhf + # - ppc64el + # - riscv64 + # - s390x + : + # The build time architecture + build-on: | + # The run time architecture + build-for: | +``` + +> See more: {ref}`file-charmcraft-yaml-base`, {ref}`build-base`, {ref}`file-charmcraft-yaml-platforms` + +### Specify container requirements + +To specify container requirements, in your charm's `charmcraft.yaml` file specify the `containers` key. + + +> See more: {ref}`file-charmcraft-yaml-containers` + + +### Specify associated resources + +See {ref}`manage-resources`. + +### Specify device requirements + +> See more: {ref}`file-charmcraft-yaml-devices` + +To specify container requirements, in your charm's `charmcraft.yaml` file specify the `devices` key. + +### Specify storage requirements + +To specify storage requirements, in your charm's `charmcraft.yaml` file specify the `storage` key. + +> See more: {ref}`file-charmcraft-yaml-storage` + +### Specify extra binding requirements + +To specify extra binding requirements, in your charm's `charmcraft.yaml` file specify the `extra-bindings` key. + +> See more: {ref}`file-charmcraft-yaml-extra-bindings` + +### Require subordinate deployment + +To require subordinate deployment for your charm (i.e., for it to be deployed to the same machine as another charm, called its 'principal'), in your charm's `charmcraft.yaml` file specify the `subordinate` key. + +> See more: {ref}`file-charmcraft-yaml-subordinate` + + +### Manage actions + +> See first: [`juju` | Action](https://juju.is/docs/juju/action), [`juju` | Manage actions](https://juju.is/docs/juju/manage-actions) + + +To declare an action in your charm, in your charm's `charmcraft.yaml` file specify the `actions` key. + +> See more: {ref}`file-charmcraft-yaml-actions` +> +> See next: [`ops` | Manage actions]() + + +### Manage configurations + +> See first: [`juju` | Application configuration](https://juju.is/docs/juju/configuration#heading--application-configuration), [`juju` | Manage applications > Configure](https://juju.is/docs/juju/manage-applications#configure-an-application) + +To declare a configuration option for your charm, in your charm's `charmcraft.yaml` specify the `config` key. + + +> See more: {ref}`file-charmcraft-yaml-config` +> +> See next: [`ops` | Manage configurations]() + + + +### Manage relations (integrations) + +> See first: [`juju` | Relation](https://juju.is/docs/juju/relation), [`juju` | Manage relations](https://juju.is/docs/juju/manage-relations) + +To declare a relation endpoint in your charm, in your charm's `charmcraft.yaml` specify the `peers`, `provides`, or `requires` key. + +> See more: {ref}`file-charmcraft-yaml-peers-provides-requires` +> +> See more: [`ops` | Manage relations (integrations)]() + + +### Specify necessary libs + +> See first: [`juju` | Library]() + + +See {ref}`manage-libraries`. + +### Manage secrets +> See first: [`juju` | User secret](https://juju.is/docs/juju/secret#heading--user) + +To make your charm capable of accepting a user secret, in your charm's `charmcraft.yaml` specify the `config` key with the `type` subkey set to `secret`. + +> See more: {ref}`file-charmcraft-yaml-config` +> +> See next: [`ops` | Manage secrets]() + +### Specify necessary parts + +See {ref}`manage-parts`. + +## Pack a charm + +To pack a charm directory, in the charm's root directory, run the command below: + +```text +charmcraft pack +``` + +This will fetch any dependencies (from PyPI, based on `requirements.txt`), compile any modules, check that all the key files are in place, and produce a compressed archive with the extension `.charm`. As you can verify, this archive is just a zip file with metadata and the operator code itself. + +````{dropdown} Expand to view a sample session for a charm called microsample-vm + + +```text +# Pack the charm: +~/microsample-vm$ charmcraft pack +Created 'microsample-vm_ubuntu-22.04-amd64.charm'. +Charms packed: + microsample-vm_ubuntu-22.04-amd64.charm + +# (Optional) Verify that this has created a .charm file in your charm's root directory: +~/microsample-vm$ ls +CONTRIBUTING.md charmcraft.yaml requirements.txt tox.ini +LICENSE microsample-vm_ubuntu-22.04-amd64.charm src +README.md pyproject.toml tests + +# (Optional) Verify that the .charm file is simply a zip file that contains +# everything you've packed plus any dependencies: +/microsample-vm$ unzip -l microsample-vm_ubuntu-22.04-amd64.charm | { head; tail;} +Archive: microsample-vm_ubuntu-22.04-amd64.charm + Length Date Time Name +--------- ---------- ----- ---- + 815 2023-12-05 12:12 README.md + 11337 2023-12-05 12:12 LICENSE + 250 2023-12-05 12:31 manifest.yaml + 102 2023-12-05 12:31 dispatch + 106 2023-12-01 14:59 config.yaml + 717 2023-12-05 12:31 metadata.yaml + 921 2023-12-05 12:26 src/charm.py + 817 2023-12-01 14:44 venv/setuptools/command/__pycache__/upload.cpython-310.pyc + 65175 2023-12-01 14:44 venv/setuptools/command/__pycache__/easy_install.cpython-310.pyc + 4540 2023-12-01 14:44 venv/setuptools/command/__pycache__/py36compat.cpython-310.pyc + 1593 2023-12-01 14:44 venv/setuptools/command/__pycache__/bdist_rpm.cpython-310.pyc + 6959 2023-12-01 14:44 venv/setuptools/command/__pycache__/sdist.cpython-310.pyc + 2511 2023-12-01 14:44 venv/setuptools/command/__pycache__/rotate.cpython-310.pyc + 2407 2023-12-01 14:44 venv/setuptools/extern/__init__.py + 2939 2023-12-01 14:44 venv/setuptools/extern/__pycache__/__init__.cpython-310.pyc +--------- ------- + 20274163 1538 files + + +``` + +```` + +The command has a number of flags that allow you to specify a different charm directory to pack, whether to force pack if there are linting errors, etc. + +> See more: {ref}`ref_commands_pack` + +```{caution} + +**If you've declared any resources :** This will *not* pack the resources. This means that, when you upload your charm to Charmhub (if you do), you will have to upload the resources separately. + +> See more: {ref}`manage-resources` + +``` + +```{important} + +When the charm is packed, a series of analyses and lintings will happen, you may receive warnings and even errors to help improve the quality of the charm. + +> See more: {ref}`Charmcraft analyzers and linters ` + +``` + +> See next: [`juju` | Deploy a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Debug a charm](), [`juju` | Update a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-local-charm) + +(publish-a-charm)= +## Publish a charm on Charmhub + +1. Log in to Charmhub: + +```text +charmcraft login +``` + +> See more: {ref}`manage-the-current-charmhub-user` + +2. Register your charm's name (the one you specified in `charmcraft.yaml` > `name`): + +```text +charmcraft register my-awesome-charm +``` + +> See more: {ref}`manage-names` + +```{note} +This automatically creates 4 channels, all with track latest but with different risk levels, namely, edge, beta, candidate, stable, respectively. See more: {ref}`manage-channels`. +``` + +3. Upload the charm to Charmhub: Use the `charmcraft upload` command followed by the your charm's filepath. E.g., if you are in the charm's root directory, + +```text +charmcraft upload my-awesome-charm.charm +Revision 1 of my-awesome-charm created +``` + +> See more: {ref}`ref_commands_upload` + +```{note} +Each time you upload a charm to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-charm-revisions`. +``` + + +4. If your charm has associated resources: These are not packed with the rest of the charm project, so you must upload them explicitly to Charmhub as well. For example: + +```text +$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin +Revision 1 created of resource 'someresource' for charm 'my-super-charm' +``` + +> See more: {ref}`manage-resources` + +```{note} +Each time you upload a resource to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-resource-revisions`. +``` + +5. Release the charm: To release a charm, release your revision of choice to the target release channel. E.g., + +```text +$ charmcraft release my-awesome-charm --revision=1 --channel=beta +Revision 1 of charm 'my-awesome-charm' released to beta +``` + +> See more: {ref}`manage-charm-revisions` + +```{note} + +This automatically opens the channel. See more: {ref}`manage-channels`. +``` + +> See next: [`juju` | Deploy a Charmub charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Update a Charmhub charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-charmhub-charm) + +```{tip} +To update the charm on Charmhub, repeat the upload and release steps. + +``` + + +```{important} + +Releasing a charm on Charmhub gives it a public URL. However, the charm will not appear in the Charmhub search results until it has passed formal review. To request formal review, reach out to the community to announce your charm and ask for a review by an experienced community member. + +> See more: [Discourse | review requests](https://discourse.charmhub.io/c/charmhub-requests/46) + + +Also, the point of publishing and having a charm publicly listed on Charmhub is so others can reuse it and potentially contribute to it as well. To publicize your charm: + +- [Write a Discourse post to announce your release.](https://discourse.charmhub.io/tags/c/announcements-and-community/33/none) + +- [Schedule a community workshop to demo your charm's capabilities.](https://discourse.charmhub.io/tag/community-workshop) + +- [Chat about it with your charmer friends.](https://matrix.to/#/#charmhub-charmdev:ubuntu.com) + + +``` + + + diff --git a/docs/howto/manage-extensions.md b/docs/howto/manage-extensions.md new file mode 100644 index 000000000..ae16f8fa9 --- /dev/null +++ b/docs/howto/manage-extensions.md @@ -0,0 +1,209 @@ +(manage-extensions)= +# How to manage extensions + +> See also: {ref}`extension` + +## View all the available extensions + +To view all the available Rockcraft / Charmcraft extensions, run the `rockcraft list-extensions` / `charmcraft list-extensions` command. Sample session: + +```bash +$ charmcraft list-extensions +Extension name Supported bases Experimental bases +---------------- ----------------- -------------------- +flask-framework ubuntu@22.04 +``` + +> See more: [Rockcraft | `rockcraft list-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/list-extensions/), {ref}`ref_commands_list-extensions` + +## View details about the extension in use + +Suppose you've initialised a rock / charm with a profile that comes with an extension (currently, `flask-framework`), and your `rockcraft.yaml` / `charmcraft.yaml > extensions` lists this extension. + + +````{dropdown} See example context + +```bash +$ mkdir my-flask-app-k8s +$ cd my-flask-app-k8s/ +$ charmcraft init --profile flask-framework +Charmed operator package file and directory tree initialised. + +Now edit the following package files to provide fundamental charm metadata +and other information: + +charmcraft.yaml +src/charm.py +README.md + +user@ubuntu:~/my-flask-app-k8s$ ls -R +.: +charmcraft.yaml requirements.txt src + +./src: +charm.py + +$ cat charmcraft.yaml +# This file configures Charmcraft. +# See https://juju.is/docs/sdk/charmcraft-config for guidance. + +name: my-flask-app-k8s + +type: charm + +bases: + - build-on: + - name: ubuntu + channel: "22.04" + run-on: + - name: ubuntu + channel: "22.04" + +# (Required) +summary: A very short one-line summary of the flask application. + +# (Required) +description: | + A comprehensive overview of your Flask application. + +extensions: + - flask-framework + +# Uncomment the integrations used by your application +# requires: +# mysql: +# interface: mysql_client +# limit: 1 +# postgresql: +# interface: postgresql_client +# limit: 1 + + +``` + +```` + +To view details about what that extension is adding to your charm, set the `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS` to `1`, then run `charmcraft expand-extensions`. For example: + + +```text +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions +``` + + +````{dropdown} See effect given example context + +``` +$ CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions +*EXPERIMENTAL* extension 'flask-framework' enabled +name: my-flask-app-k8s +summary: A very short one-line summary of the flask application. +description: | + A comprehensive overview of your Flask application. +parts: + charm: + source: . + charm-entrypoint: src/charm.py + charm-binary-python-packages: [] + charm-python-packages: [] + charm-requirements: + - requirements.txt + charm-strict-dependencies: false + plugin: charm +type: charm +bases: +- build-on: + - name: ubuntu + channel: '22.04' + run-on: + - name: ubuntu + channel: '22.04' +actions: + rotate-secret-key: + description: Rotate the flask secret key. Users will be forced to log in again. + This might be useful if a security breach occurs. +assumes: +- k8s-api +containers: + flask-app: + resource: flask-app-image +peers: + secret-storage: + interface: secret-storage +provides: + metrics-endpoint: + interface: prometheus_scrape + grafana-dashboard: + interface: grafana_dashboard +requires: + logging: + interface: loki_push_api + ingress: + interface: ingress + limit: 1 +resources: + flask-app-image: + type: oci-image + description: flask application image. +config: + options: + webserver-keepalive: + type: int + description: Time in seconds for webserver to wait for requests on a Keep-Alive + connection. + webserver-threads: + type: int + description: Run each webserver worker with the specified number of threads. + webserver-timeout: + type: int + description: Time in seconds to kill and restart silent webserver workers. + webserver-workers: + type: int + description: The number of webserver worker processes for handling requests. + flask-application-root: + type: string + description: Path in which the application / web server is mounted. This configuration + will set the FLASK_APPLICATION_ROOT environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-debug: + type: boolean + description: Whether Flask debug mode is enabled. + flask-env: + type: string + description: What environment the Flask app is running in, by default it's 'production'. + flask-permanent-session-lifetime: + type: int + description: Time in seconds for the cookie to expire in the Flask application + permanent sessions. This configuration will set the FLASK_PERMANENT_SESSION_LIFETIME + environment variable. Run `app.config.from_prefixed_env()` in your Flask application + in order to receive this configuration. + flask-preferred-url-scheme: + type: string + default: HTTPS + description: Scheme for generating external URLs when not in a request context + in the Flask application. By default, it's "HTTPS". This configuration will + set the FLASK_PREFERRED_URL_SCHEME environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-secret-key: + type: string + description: The secret key used for securely signing the session cookie and + for any other security related needs by your Flask application. This configuration + will set the FLASK_SECRET_KEY environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-session-cookie-secure: + type: boolean + description: Set the secure attribute in the Flask application cookies. This + configuration will set the FLASK_SESSION_COOKIE_SECURE environment variable. + Run `app.config.from_prefixed_env()` in your Flask application in order to + receive this configuration. + +``` + +```` + + +> See more: [`rockcraft expand-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/expand-extensions/), {ref}`ref_commands_expand-extensions` + +
+ +> **Contributors:** @lengau, @tmihoc diff --git a/docs/howto/manage-icons-create-1.png b/docs/howto/manage-icons-create-1.png new file mode 100644 index 0000000000000000000000000000000000000000..b1460b0d854b2f261236360109c6da23d73e9a8f GIT binary patch literal 157228 zcmeFZRa9Kd);0`DfP|od;2sj(-GdX{-Q5~@O@ccF_n-;xG&HV_yIbSdH17J5z4v?0 z`7i#9_u{+w#yC9|qnlM#&#HRnteUgttnM&Hc}Y}c0%SNiIMgpvV#;uEuNdIqUb4SN ze7=)?IydwD7U89;>7s1pPUhfbZ*FC4M&{z_U`A% z3E<$6Ut2wQOH)pk*Tf#cXk=<{Y{uvTaCq)792~!phl7!cjhPFXv6+RHodCsAQyT@D zm8k%Q28SH8oP(&DrInPIlbMQ_ysC+pjR}t_g^(aJzX$I#0Km+}h|B|EYv;`CAwcmL zF7NZ_Kew4E$o{J0Vk1DIDW^y#YVTx5#?Hvj$jl(_Vdch3A&5-I?__Gut1Kq*55)6- z0u+`mE)Kj*Oz!UPjP7iV_D&W|EId3sOw6oItgH;rH5i;d?OcpJ80?%s{DJrzL(I(C z#L3FR#me4}><^}qvAwH{00qT!IoUtpe?;&vbUWvNNO-1$=}!q03nMere^z&~GXMXn z{-@;M)&Df+RkZRjv(*%{0+`u3KX*ZZf`yrr=P$H>7i;}TF*hsoU!{Mbc)tLQEX*{l zOkFJhquswtq|B@=ET6~n|7oM(WM%fu!k_UzH=0iS6!_fcz*MA@K zzvB46bp4mE{}luOtIGe?uK&{YzhdBjRr$Z#_5T)K$p3cl%~}JNF(5I$dvVC{EAdiAjxaw9NJUN z_n7&iFK}LABGCC5N_J;ctKW-#7E=;?rPT2PFA?n)gFuT4c63Ev)enN%<$+09j0>m*GpsYjc9nxWs|benC_;8vl$ zj+uUW2Ly^Tn5a2om4gh)%2#TQT;vE4jFab48`+YbaL3Zib4s=-=G3sN6$QbPDj(i! z6~>8^4(Be>2DB#Aj=Kz&Pf+_`vX1)bORYebi1uUsghow$ic!2pCN9zvt;b%V@{4PD5K#=uLQ4C5P>q~S`Fy${M6k230VOa=~ zlq{5vs%rfy5i(Zx?roQiA#kyXiL+M|JZPe9H#aW$yQa1F6G}_1FtsAF! zLRR>MoIe0k!FKy*z3I=tt+uAFg)1Ag;8=93{`0<-P`>8q6G&pGJQB^qd#ejBmbAy< zeL2(qnpAG3;FrBCt`F$0!~p4p0TaIB`((NZyCj7!9r6Stz4ltk#TVM7%82iTv8{`y zI>=r!tPkR`S?21EMU#l@sm6?un=#8~eG)b4nU&ij!&M{G+6L+t8;p2<;foMM)+#^8 zDd+v=JwcZ};M7g+5^iR4dOtZ@Q_0F$+nJ=>8#;RgeY2EtQal*!+t zY4omJ36;Bw?ffCCH{C-C;Q@`~_6IP*#|^A#e$`sr)t zq{Cy)B5)O>M>)nSIJ((mDO_4ATLpg_s^?Z`954jEWh|@lU=kv$x~FTnaYUAHHJs8q z{~+#GD5)3g?UbxzcePu37dYZXzf&DMD#w`D)9~YTMU=JjyEG3H%CFpPJ*-FS1{6N0qp9mb45&yXBpNhw#Y98(i%ECz%7`r2+L1e0Tg~#C!LZ#$o?LdFa%LsZ_ zlG{d=P_F7A57ESA(Dw48h=tOKZVQf!#A#oM5_P;2l)r?0FG;|Fbu@&WQRfY6wy;Pu z1G2iL5~9;#?Slr>%~?GjjY*zmHBzI9lVY^zNj>+}_HxH5L*&@-*PxM*gnSp3O-abU=Gwt)}`XSHc0-36tM=9u@MZNz4oiQMHg#TsQI*>$M*SOc((!Eexk? ze%Uvi2}=s3{Y5@n=V9m26d4|8gQe*DGQ=-~Y~RrxGR;vZ%bmkPo4<;06!|Bzac{Rb zm!eF=w&Eb6FB=Cncg7b*b{w0x8>2n5g(RME;^L0$@ghFGIH|`l(<|mh#v#oU`)o3@ zXp9$R_X;J0kqSl(_hFGr;)JXU+m79_&pPolzbHQQy9Jyds>=hVfOp>LD7HSCMos(I zJ((jb)2)>(loN{5XO#{lGmPG^J#`|JagH(-PuM7bUlA++6f)8+?SC=wC+2aP@zZGi=Kk~vWFuJI#tE^(8){(?J^PT zYXgdFZ_$PV#RYY@-x{hCy{uwDZV^=PV*iAf^qAFVW@DFybA#GfggobshGrijs>ggr#5aU zm#3h`1;%l`l$Z5yO#cO(H$)6BSBGyDrBR)NrM6vju=Y`C>&83fR5h2~_pK>J#d7Cx z9)L;R*d&R@!a0cA{HnY!m3|TZZuHREbdnZxtNJi;Rp003nOJz5N}4Bb7al~gzV1Wq zdGlps?0McCTKLNMCedRJYkVGAy3=~{6fU5tl#MhEVn!}#dZ62353!U?6dp(&I!pB; zGBp5;P?v{91A83sr6fR%CI+K8Hs3NRSqBFB)L*gVD7>Rl^ep=thUIt$HDj^$HFojP zp==&jwfyA`D7gcd9ax0!I=UzZ7{C|TqTuFpYR8Tlj+-d&k{YXk8-rq3&jhD^Uk6An z8O+m|cCAgv76N-JlS?Y|7^jO+XccaLyCe!^hOLQMkZ(O+ZxqLzSelC>QE-E7gv9N7lh5xwK_+}c&pHZxOwI;vwdN`{E^(m@d?l!=%2-a3 z*U}fb?yDD>x`@4~kJZHN>==9fY<-60GIdCqeGnN5_)KGXOa^xkR2-G)>4R**Wj1O{ zYZRcP8g5aMBk`9ljPJa1&>cwYeju1keL=|3BV`7ZcmciP{MQUu*kBq2^UlXjln;PSzay)WWmNvzjFH>pqK&t}@13C8FOOk9v zGFmkCUfhwd8$BA%1;6(%FBTrI-9Kz`556g(6rrPJVWh?3MRq3gAYbT!nX_eyW5l&pU zMAG-R8&YBWi0`%lrM%_c9}f*G2+4r@*&Ust(b>A2<#)_PX7blWw9Frl0C8I4DoE4B ztoaRi48ed7{&4wsm~R7BUAqTM&_-^^053ngiE!~FmMe3UH&vS4#GciM-T`Q9VZFYE zinQOVajiQC?26TcRa7TZC*AW_dLi?sMq=wxfCrS9=&`&oV{MD)IC* z$(_I`Xcu&NQa{!_H7@@jE}~cb)$D zD@x?)T3NvVGRBG}&_4K3K4;W0=MG_MY3=N1!Z!flgz|q3E7W)qt>kKf9z4ZVwh;c*20bcw!nmi zr77y2Z*Q0&R&P54MH+S%4)2$whZg$3kV6G1^*VE?l{D5+Jx;=ZWTtm?a=uYl$gS>{ zxyyo0Ny@S4{?P{$f?3xv34pZpcU3htwA$YxhNQB zm-tmK`2_x_gYSD6N}Fm{%UDr}sUSl}m0`yEY1~uj z(AKmnd$-=Y>s}aoxcKR>$r{2Q9L80h%wfau>26_84M|_&pyj*CqF63tIU$>I3JE&A z!oeyNl{HqC9+5^K%F98MwYtca%*=6zgx@75NnF$s~uv zdu7XozVJkjz&i2yA%vk!?JrvGFQ}w{mjooW=y=%JYXHeVv2BQnXo=s`>~mfVQdzTH zG{ke~PIU5AMkVVnisKUG(F^-R=0w40@gZgW)`+;u5mXFDA})RO)-gZ7q9 ziK8YE$3N6olL@yWsWb*IqAAvx(52gmdiWwD%dy)kvYL>CDHni z&B1t#&*~Ovm-khaSu=t4wK?ne4hmjAJ{A-XQ!h4)5h;(SXkxxQy>vS-*NcmUq=eP0 zJNVZ@BDM8(EIGy2eW$3kSi23M(*v4m@p$LN%qwJXduqm(b;K|X-u_w7q|WDRiNn$O zf`JTKy@BtZxmr*0BeLh**&;&WKz8A+?821PtOs(4GmQTSaBXV`r71}5n#K%2f z>mWC#YwmC|r{@hv*wCdIX-6UMw5EuN&KmAkMz{N&c^Ml4wqKo7+P6t}RJ^98r5?8Bc+dR@p5}>w z!zFOB8s8ps2j`C|t{1#NbRW?e7dF85++rf+xdSp*(=O4xUZ2D>J+@vBRhA{raa)$wUz`ssk>wYRT1y>j-V;V4Ha^b`l=g0 zet}wsbOj!?J|%ALEqndQdZ5r+)?S~XLj1@UW4hmMtlE$TRJZ$y`IeMZ?+diVVTtne zityDv4uCLghZKs!FNmH5O#7gaCaI=`zol;(ah_kO=~Vat*moyecG3nI~JtryoPRW!|0 zwzn}r6rr;_FoFIO1t1qQFX3-kim;<2LWF`qYSGrj_7!s z(CrGnTacWlB;BRvfX-IY*(z@$3}alNHtwE$*^KFZLzpnklO)atRx$0p`>ma_-Odr! zxiMLLP`cJ$s!ZElkbuCB`pq#R6>dYb;XoFrca{a?r<4sZJkOcfvx>qgN?$DcRr%gU zlUHPO*qFC@BMF8e72^8qRo}D8RY2;NJ6J-RKh1;4M-1a^g=2@l8pS1538R9fsaP{Z zr$zo)t@Z5HmZ}0o5<i_DO6Q25SD~ZH zcEa1jDw(^Ukgj*P-nUMxEGecg%jzCd9eJgf z$M%vQGh03SeBvl8=(t!_5}m$MtEY@>bFhO02AC)nYgG_?qGx=L& zrMOxYsb%%_h^MF3z6w9TKii}AE2LmqrpMF@Bs_X$2253oJn^Rp7m*SvNao4g)ax3t zZtK5n&j5-KBCVx;-f(8OhyBM%Dm#nm6~OvCcZn;=g=*d)V2Et42)rBN zaAw+66-YN%f~O`XC9usGJ>9J?SMF_$oylnL^udbmP%~CCYg=hxm>PWj;ep63%*;Rk zN0Av@3Y5Tbw#M5ZZ&G;<2CHpkUeW2V-6w9=f?^zNz1*MEP?)qCZ+1iQ*CN)i8O-go zF*VA;52wDHDeXyhM{x$LId6`a@jva`*=7e#^zZ9*C;5Kav`_Vj2(QH6O(Nhtnp2ny zRb=t^8nR^hNu2oIvOC9e|J4BYs>|36!hl`gbf#G2TF>snXwouljsV3Tt_@S15HYwk z>TiSc*~W&j!_ZA4xW2yL&K^;qbg!88E{`PKRbjEj*$1Xg(U$P~0zbkKUpwn7=RHb6N+Vjd~zydMV>AYW+ zbj{F%9)(ET?ck^UFUQlsk~i|1gG)5R373;qPipm^=FSTfMVdK`B1!?D!(Q0(@^w+0 z88^xuEG_mVXo=$me^RXpd8q#eE+K2qhSg2dq;Fabj8n0FjN z-Ltr@r0<6(IbyrHMVz_&gC`@cv{kQv>!|_;2)y8a@!r|Vb7mAB}hakn$0 zJp0u(yaL^>9K^(#57Ah1Nh^vv@?Pb`lr3S?(+`hK$gil8UnwUsRhf}DQ7fn~XzZ7^ zzWYPCsQb5dM=Q;yABmaHDQpGbC%z)Z5EnA!o@l2fMOB%lZ(PtO1Se%S z@i6-eZb|Spxv_sU%YhrTQ30BNtf(I`^b*I2iN>hvWpixCh3yk15qkcxn6+D3(jKNc z11tH>@fRL=2N1A%`uaX@o^qkvZq;2NhK!aP>ss-^>mEo^4FixY-BmM^BSbYUUa_r) zSR!3;ENa#iAq?6WuR0mRr1kewx|WLGC%pGO265)ptAlDk!gD_x#z5}8ubr+OcbC(w zR?4mtS2Hs+@F0j|ZrDV+fVaB-C_YCx&quVETV^o zcCr;Ijs~u*YyyQgd{yc$-B2)9D)@3Yqj7yy&v>6@tLiAnj{R}{``QV{@$cIG!eEdD z{`EahlDryZ^K`aej&gs!8@8h+F$C=ni?ldNm1LVfFkX}yt-1V6f16^J=FuXtlzLKi zo)_J_C%7+%kC+7Rq|?WFUfx9RClzgM7jue>zn^RjPJnjWznSkt39IOxV{yBm_fp0u z#eTbs!A0$=#dlnI)F|QyA#?WAEgw}}7n!|&)clZ4mv+aWQ0bV9`LkUo0^xZ)=r=o# zN+{wb1wjbz;ig6XlDP%UJgKYKNjfElG{?QOSx-JL#E%{2V`3S!N9yEvGgfwme*w2e z^=uG#ViNKT-Y3D!vo&4To1~~Gjd}%bon3<5dncsyxH9(%CW`Ex@aZO{t9`qA@KqlL zH{|`bdMB>5!f<}EQv4Cn<0#f7dr^fwB{njf<9c8|)1%(3+0!HM!omW}ux7$;$7tes zmj1ehQDKZEoze^1pr7$b(?5RI{ONt996O%=QaR2-fba`JpA*@l<8_XU zoDUHh$(ny0f!NRK>V|2E0068O2*g4`#OEriw!7ypESCER{h6x2KorN)e43#y{sIbTX^<3iC;tc$Gs>^V;2HZ|g(w zkcR>nMM0AmW0x3sy8>~~5&gxacYl^k42ciOgEnRu1$TzM?NF}caI!y$A_A_7o$S-X z*;z*ThD|*DaW%pR@Beai{%p0rljV2zEy7+>gcLTbp`HF&L;kspELG7+2w(DlW}CZY zAv3nP7sUNn+4F`L;ALWzg1mHafFR0i9s1@k!~b{7z>M9n@>B%%L!PTtPTkqW-v2>- zZk|Irpt@>$NMMB8`mu|FV!6-p*MD?{QUre_+ za}@a@xIQsIFD@gKPV_Rv^q!py7;cO9{YEj?@8e$&jm4yO?h;=ZXM0{WziExU3bA2UrvM;Kp(`;zN3*uNx?>=vKxtZkbT7XgQbIaqaZ_!*3LBKl3an zk|QBMpNYrx(My6N!DP8qTdMZb$(2!Ysw6gbkt;NykIHaJUR4ViG=$#6~R|=R6FdwAyR@d?e@m2 zZb{Qu(XL#!UO5aLkRc-`3ScW;Qw^KhHIB&4dk?&el0fpEomm;b^QGo(X+x2OKRK-f z?EJ(tD2Gn0jDJy4VY@(;V`pa{pO}!Kd^amt9J!Iymdv)3{0a6l;MNCivuX5#+WxuD z)GXJ;*qqpN0~m~$+bW5}DpeI9oLQ4Gxt+A_YKzVh4TTa~+1QYBoDRDR4!L_u3@>Sa zPW>e+2;P($F2TRJGV&VvJM@P_siN?fPTc817GH~yLzU*}ab{W9yVhd$G$K*xp4+|E zZnCx_*q1>GW$=Y}Lw;hZE*hb~rvUJQDlnbmO}6{z`P5&IRC@8 z!P!mM;~+!!4Tm@X!{FXXYv5g*FD>*5a&d1QFj+x#_unH# zuU0}@%dO*m#@%HPk-A>w_;lZhYsKj%>0@6_6NnlaaYEY0u&uwSW$6BrM+1rH;taA8 ztTJ#+u)09LKyX=YHSNrQIu?IW9pFK4qdCa#B1E^-0=dDTrI%JN+$Fp}$AWBqba(0F z+PQJyHse!vhQ6L+sW5R0+@4;wGFZ=n1}u5QfS!VXD}5)m-so4>2`*+7?JXN*T=hx) zGiJzRmxOH|mFaI8$+9o()$w5amN4+Qfq~P&E9)|=qg_gZuyt;R2i|3Ep%#YI#LOi@iOPHle0;}Dfsp8p$=^b)QOvUuf5 znag85uS}b6uofhD_|u8H;Ku;U&&J00U#qK?`AN{%94K*qiP98y8jNY`h zGwi_Hbza7Ds4*U*U|Wo#(${BYEhzXExpC}atXH?g03chX!lRd7pdte2)Ub)M6ZANm z?1v~TYAI#8dg@%r3Z7&#RX014jy^oZ@VC^UHkl$vB)0@W?F>3GAy3Fx87I-Q0a zrZBw_orMP?_ch7J>nZV}QfABfTU!5yJAM7%8YxL?U!8g^50Rbrw`Lvti{7SUhgSMe zq8%8&zQXZ$n17G+R>%w2|Bm`|!cMC*kB~vO**s_S5M7xJ&)$^y{o|wDSc?Zr6Ps3j zZALHd;~)+U-bAF$sX=srzTI(ld2klDe6^R5v;YTP3FmYkNl5bINU)!n$%;Mv2b=x+ zw-Q^y%YJWU6GN3x(-yu-6$WnD(;xLS>RXy`Mv-oAKG+NYjKmVU`_RgNN9way2(~)b zW)e2QYS62kLIsVQdgxn^X&D7P1=)&aP9)HeJD-;O1nkXc)4Pm%#*=-& zJC^`YRyEqdLQrov^|GytWPOWCD*N?#4TBMz*@w{COid;{_xq5B2UtxCUx%pVuy&T` zu^@?O9=-w@^KVFl)%i)1Tl@!zGb*Xz4xV`w@HL0++R2fBJosu0IC^o}GDA=gfZ!A2 z>Tn*fbM`3Mc~e=?-+t8LqO@<4QexiYz8+7nvZqyO%kmy6|4D1;YcqoEe3**bRXaRedJ zV;+q6{zBW$h1qA*-m}#Z##kTc6!iGSUmqh_PV$mD;*<3HshD&JmZVAD1_kv&PdfLf za#TC1LXN~`8K#1&#Ki7IGD1!-aY=n2b6qeORm1jNG$~ZbQn~_B;;^FAKec;Me9?d4X zhQH!UX7;}^)Ab!pWcS_1wJqeHE#MeNO7k8>P}F@VRzwE&JvSF@Dh+s#zTES0N5%~W z=<+LEJZFbyCd{FEkhMb%5lePGxo@R1+pfUlbLXfGzk&Xt7JtgmVo2tXX%8Y^n4kf9YPbUpQlcoyVv5wO7C9kwpf&Bvx(M6xu-{bx_wn4cS;qm%Jn$^v&Y4e?Q@eC zrP}x^bS>(1_76Auk1p-```ueUyKlKnd~eS9mGju_Dmd1Rw|!P6TJK5XaY-K${O8{H z65;083@2X+a#cp@&VD4Qd*kl%uA_VBEZ?$eYJ;r#4&m{5w2bKIckvy+uQ3ppkFME` zeCQ@TXE!F33z9cCXTamDA+T3sXIB@=J9S-!PBxWn_IcPdLIvQr@vA#dp3tPdo^8gLu2#h9~g^rtR;s^J$pzj0>Ah(J&p>$F1=m@{Oq&@q# zfEpZGVZd+jd}UhQWr&uOYV3}Nb*?pU2x;N`aXLe-cSt!TEvo9A&#k3DZ8)kyT9`3h zWZv`>ZsWmRGnNi238&YnTfHQMxorAZFOI^}>9%`|$4g`SBMzv|vHSxfE~7zb_K{bB zR50w~cBV(y;ZW60@E*ahE;pd{*n8#0d80?c@A%M)l;S$;RQ3{(<=7tp46W4iln}#hJ4+tv%^F?BCi)nr)j1t?ib@+*lPVqQ~moiJ@H2 z_<+)4x}665>>(C5?2@u`sI_;@=ED%Z#bG?$XWu&9UGJ+e*kSG30s~k%+B+b#@%bTF!o;h2DAs#r*oEY{Mh&dD#i7 z>|lXS2G!0FamX!$fxiQB{XDGmpjO4+E60mKrA$J9X)5p&w{g_o{1dAJKrmTmgg*{x z#V3pt%00qO-hK;d{anUc^CQ)L5K(`Af5R6%^r70CNhUgNq*s#kgU{n4kMZ}eg<1eZjNG0{RqI5`$V|K0`zMP=-i>HP?HKx~@7Z;+UfzT+2+} zY=lSHy$OISf1K(x%*bUi*kxU~Ko97iLFt8WRV*=M$K7rQ9?6$(k#8C)ZHWx4N!hYr z-*R>3?wZ2AqY+;y1kBp--v=OhmQSZw!&q~xqP_G)tQ?J2)(hGEH8V>Z?~*Id&PKC^ zFQ8B0i;CJj-yqkJQ8&S%f~>IPBfl)6a{W%pwv(6J0(U41_ZKw!%gYj#wUq7wO4W@V zGN~47;9lN+&|R{>^R4SNmId?VZ1*5^-!ZI5h{ekABJC5So)g}5X|6!bjoFg_%zc*q zgP)206a4E@o){gbfij_U9kN{leP1Zt4k<^7^#+5rDd4Zon~31-9!_j;4nPYjps!bI z)orTAd-m}0E`8F0i$fLxl=@!in%BPqePTy@^_|_xW^<;vJA0+O!thro{N+aK+Vsa0 zcjW1^rR@q9x#>RtY_ABJa^6Ifho|i=lVrz8{hsBDdMZ~o`HyT}mfIJJO%3;@8!6Ve z+Q9$u3(?JACvg(b5}euD_6>Gs>&*4_7pPP%i$}Np+Z@nFg#7xS1R7$3r9N8d z-OWM?9=c41H~@ zFlb^N$;Tf$<~~@2gboIFAO{0X(hwUMrSSu9C5tbWx2hg@L5G#+jrs+HXesm2IjCOEbcSCqs?HxK3AjI zVu;7_3lH8h0-G$`i74mty;DNjqW)ksjPJI;o^1|(Sz_3SpNkmWF~%}V+r?~V_P4;` z>2Zr@=Vx{qc&H0P#d7g3yyxCCY*iMK)VEcl4jzfyyq2VR6%lScU}z^}lRHj54v(Cp z1&jy8xjUX$P2Rvr-AryC#3(LfMbv1esZl!CDMT_E4AbW7;Sp$OADyq)bRk$CT-$h$%4GZb;S$X*4lJe}yjyUd& z9Zv)tu$+#IBMxfN1X=Xj7M47piOzYd45pwMSHG;=^Sk1)$=aH~R5**C#8L9P$98LJ z{=sWjSZdIEQWa#81u&kkKI3_)fNX|65PP&#Vc~;vH@L4REm(?tKl@H$d&iCuAyg)!5zSJhEIy}*LIr|{D_U1#Y zx#9Z0d3tT#;Q;?~@Tte{>tyFC%&kD6eU5%A&lb=u@b01A@4b+{t6Pv_;TK6u{s^3C zs%B@TDCx*Q!N>1p*oFl*w3V)x5)mS{m|u_vck_$XSk_X^hCFYtY*AbKHxyty(4(er zqoA0R^I$}Z6{kX+>ElzIyW2*vc>s$Yf%7&eR+RT7j#Y_~0gRT!+X)@w^|W)%@4rb3 z-GAe~!^+c6R#1afG2d4RQ(Ey|KxlRz6q=Jq=#gWpX=UN+_sm4S-`acdIl8w=HJ&;C zm2Lk~#0hl_S1(@Ke5H`u?J!6!1tW)K;sk4zPWc~P_P}LI9TYR9x-I8PBzaL z@eH^2-JV}Wsa|p+%zj)Em`rdXKhn6%o|d)P5AV%C-SMb5lm|SGv9NC3czz7_QWxaN zuDd6_+Rtsp+bk5||H$F5<-tIDa|uj2>sD!QzJRmabBXo_^JMT)b)N}&hR%wzi2f+a zvAi|L8F_EP$34z^v*>wlLHLGB^Zr+H48C4hc2@C5I_WikHVcVKPwyv+Z_>>i zqg9P9%)WMgoO0uC{k@aZT?{$5fm>R$$Li`Pxh3Zgj}}$Nqlf05S<=y|S}1_wz_O(cT*`DE;FRq4 zhE^Z{B5$GE?Cw{dS^X{(g&BuM1TF{HU8Bbq8J4s22#=>)0e;6PFVMtsI8DB-Gwdqm z;cgnliOc+3_f~LZ@rj?1@cm?xUONIC^m;Y`I#FElSDa6zSK=)lYF-e@C(*!-7rZx| zZzW&ys-sO>ZYP4DMkZo1M!+i3;4;_qHItiudYl;i$f9t!IX);GpsdqEO>?^Pb-0p@LuM2!C^g$N+6unuaX^ zG3#NLmoc3!I$6K0qaz0B$?>%8L+(8EzPS=YF}sQp{dVU2+4bq{Xv7`+z#!y_TFCbd z-_Hr+QSLDsa&vl0fx;(t_lcp=i1G z2a>&i!uz{hLi*eA!)_+&TA_>&H-oq5nug6hi1*2taoXcz8g2P6 zjUf>Dt>*%aqlyP;iW2B>{M?wH-K_R~*di!w-BBKy7(LFk*srWQ<`fuYdh}w2v}}st z2>RouR|V^}7HxNlZE0yheoUuOmkUP5GjWFmVaXhG!=~(fqmT|gg9TC=V;%yXpLw~_~-^ql4!azOyA}sqyW&! zb9_F&C^||r^40I6M-ufjRv04B{`Zob&xs+?EjBk`bkfZe*gpsNj9#G<#?TAMT?ye+ zURD(D{Mzv+)(OpVbiGnIf*gO2d#%z(S`K{|CdMKcP~XQccp1RNyoYYESTqw1bz+v9iHACiIH(Pq-9R* zR5hdq`O~2w&=xD%4P}bZOnVbpk2-aSXgqRcHMZCf9h`}YBnjI3Acxl` z^6QaMyY+w?BJ0`mRa_z8aJ^fy*RL#wRt32Hzio9O&fM?Wkw7RqV6TMnh&R1uGXI`s z+W8L0s?YqCxrgPBy#a=FGloJ)UX;5zj&m{FGyiEqfOfCV2|Ry(9EejoGCIPsGbRU&-MOF??5?dhEY4f%rk|< zhI6srKF{q*@RNKR{SBH)s|Y*{r|>)c3eL=!IU0+?|Iii7qiuu6&N05Q@SQj+I*e5( zNqn!tF_NjGZ?DV?_UJ2{E{HW*TSKZ=E;95d(<35SnWtpCC}DFIThgRVTOWJs5j zYASfon#_sk3m}6DRKv>Z%&!hJuYCl~M^j~#lpLn6kjgmpGBc+RmWL1Z_)<81&1cM_ z1G}zY`Xv|{;vp8|t2EUrSF!5>HFgLRB!TNoB|;@01q%o-bzv{U4ISUaIcS~|Z~Fvw zj?kpl3NR?6SjX-B&TTDEDw;IMKnoGW`+kmOG<6>0KrMFm?UUQlinxU&Upe0;?s@z# zd6_%y!D?zonw^L3X@P2$exjtomC-z-?tPr)(b%=CdM` z^Bq_wY`>JVU8#*jkfhITZhqm4A-1y8>?i^I*9q{q~mEoRkwCTy;h; zt!pZH`VZS@OS_gwg}sQ|4E)BiCqKc87*F1-?l{bKpoc5Z@QJq;JXN9kQDJ%S%RFjU5cuU;qr2Xz5r|*$%fy}qy zx=JU{zjBUg!u6(2tBa>`LoD$#gR#(U)wLKFoLB4SqI#oIA-A# zU7=;Q{Ks%{a_Q89?mXt=2n^`=$Thj&i*@W_E#>#Ygu7lRA3^$FaqScF$3&dz4;ltt z2PkL5R5>ls`KM#96wj_B3Cbs8cm2+8bRUGLgdn7cpSFHmO4|CK8CIlsG{J2istJ4H zbmg1HCFSb?dp@f%ya#{C10!_So{R_$3pr?af1d&y88b?vaqV{0VJ!%EyM;ff@9O@_ z;Ip?kpPrsxfFEPi0c-2sQ>zNby#CeRD6{LW?6VAi1F)hbo{t&lx?LUl`CsWyUb&0H zp6BcD%_D_J*K?+A7-( z&?!=72qRlLW1oKyrPiI2px`(4oB`&S^&5N_+9=$& z>|2+yJ{~&Ush^LhB7{tJ>AWZrU?4-!a*7z zF8v^xt}|LK{z5n1SS|jfjUFu7Eo+Ey`o0%xg|ewX99s)W@5#ievX^XPce$#vv1_zv zr)MV}V0Dp%TxdtNRvtUUCQDnS(fBAUUJ>m-K7N?_vaq5pd{-=FavShL9dVL$vz^Ud z#b>CAM$6|pRZS+^H1lrZoIYNGC%&bvF|J5sC5%q<6ijiaCoEF78_o3lXJ&kFjJ6*v%Z_XNqgx1PrZoI1sV{#{f5$C1M}9d-&lnhg#L zZ)QE&b6RM)e7N>t@x6*PYW%>!o7%d%7poT@iUn#Z;svtloKY>_SJhT$IOd~iLEw13 z-6-*hnEbi|9w;l5R&}8N=eTeCJ%Dsb0N&zkSz)4(?Chdkj#iG+)a~<75W24t4UM z>xEo$JQV^iCg+ClsEf0tE*6uN&JG}zDSB-ooMZ=Wj)T9V-EZi}ft1@$StVr7ZDBWk5e?)5!0DGM|TS zCKqo_vF8)(Z~W0O^kUrSG}jCr5=lybn{HDOynbaIcb3?9Z%tFcX|n*zs>PpBgA&=SJR|(orFzLgv4G7)cdF(yE{`m zuDy2h&aKUYP7e4}7iN6kYwhq6Vl<-B=knRq$btDbtxuXMW~mUfj3e5_1mrs|Ukf!R z_$2;qZqa9w-6}?mrR=qtnVDFglC`z<90)*owlh53zp>r~X$e>W@xJ&5hv98Qh#cev znFwhUFt5>oh|= z{8eeb))C>;^FoKt{K7W=7b(`^jPKm*-ybRhCj!N@AK3s1_leT|8y*fp5dHW}yZ2&h zS6f@gHiOUm@4j)KoOF(2pwTj`sFeV6K-|_q*<&sZ0qPjJ=Ao+*10*kbNGW z*r2m^kS~91kSk}7o*pjH>!J)mjsa(C!hrLU^z=v-c@IsR?RKV(<3JH!z7E{fx$cD=gE$s;ge{!pTH2uI zFoc0IGKh&eCyYO}ik!3PD!PUiu`hMw5$ zsAEP#3eSPh6@@q7VLRLfh+P)9A$QA$_Hn914v2k*I zjDrCU&Qd1<4cv2gemwtHw=nP?E1@aTY(B^MlIw9~@V=Mp6)sK&^7TZ3x)C~CuHB~= z`f!kZdGYq`cAww|+v3Gon~89nq3|gUK^<;ujVIeFdjjkY*9ab@+OBImeVrR%^x4Z& zt#1q=zuP08lGmP7D_~-!`!&T^XJyahcC2Uj!86g98R5yu_u5RS#r6=;ak-b2_f8Q; z=$Z~AW5Q$;4Dz%U`jt+Qqb;;iF{T5RIGzrbUg2N1M z>_Nkrs2aJR-%+3c723Vh(MD6j2l!Zb=#93p22`;kK-6$9C6%Hm-r*?Z%}jJ>cGcS; z+cG=`7DNxS5~&<`9n6h1Sgim=Jf1&o(>Ev0dt#g&-g13W zQ^@iT6E2QT?JAXkUT;7Ou#E-SN26itr@>vhx`RzwazKxmn3sV6!h#S~IPb97;$rxx zjdJ(Y3a~pyo%{PVnKt_l(D-r_=QkO{y&ay=8=^!zMCi8JN13MEU*zW%Fa zzL|ZCaIq$R7UgGh%4@dUYztik*+DNo(X9864Nt&yO$Ooljj?hGK5QlhPETJOr^I!WhS)!;Fa&@PvH%SCR&%?g@3k*KmUE z6M@@ieYSl+8XrHOR~H{x`C|9)-*%QVqnNvS?mn>LZf5N+i7`+XT&>9x2K!iTHb~JT ze)A(eG`-|59>z-W|ZqS4Y?K z65{?a;J^AjD)aElfZFOm6Z)j3?rnQr9TpM4NV~#i+X7=cKGO_ZiFf%mh!yI;!8E>b zbKeat%e8^p(P^+UfEq_U)-O}QXZ>S3-~0N3n=j^J_M<~^?krzR+TCBm@^+cSwuZS= zHHOIM&1C-m`R=y;EXXJHietdPlvK1C)^-863>Acn)9EvoNSU0B(P#_u#A4P?Zm~6O zuX?^&ieukjwsX7;)_ngaX1DKV@cuQ27(}bVG|GrGQn0xVE*AVQCI1`pq}Kh>-2`JD zrD(0$244v5K(-!bTD4EqzNRq$K#)@05Yye#^V@p!yIRIA8-WluV~cr$%2;IjJ@`GB z0A-(4t-DI2Yuk=r{A#_tDk_KH?KZ9FM_=1#W44{|9lox6-}kO=A%XBIX#hv$tmM{)+QXx!WGgtrunQsCE=!Ic;TUuD+w@PU9}|O1 zbXHZ%PBTM+OM*8CNq;$7X1BMeYkeQ+0nr=Z=GjRl$WZC%=$`kmdqzu$JEP5VeIwV` zwLk8k_Zh}mXiB@hxWDxHJtLo2Re(imc6D(u+XH@8XD8Ye596hJJI{ZnUnP?~Eqii! zX$F0*GXr9$CrL&NN8XWLchY1Scx5=FXVAMduW054jn4Fn=0*I5I+QY`O5_CT3X&|% z_2BCudSzn&;pV3JvLxsYy)Z2Z-{DI>t|Ku}Wp!+QZqomfS9)2nQNSxbdc(4R_w@UK zmd1RB3n$Nj-FH{n`9d=cU&g!J)%^-eIIG{RJ2JR-a1C^MEK3}g|{T33u&p#X8wr`YGt%yH!Gqw~OEaHXHNJtCF6(A)c`Q*)^Ud`;rnrc3i8M1TBW&<}4LcBI?8=RwD2&F1+IqL& zc>d?{++#Z1Z2gX3$>UaljMI%1ccQ8D@!6=5>XSIn?csKcJjwn2qS5_r*;SK%1JJ$` z5x-P*}>)RfMQRYgQV9J_- zX#vj`2kcPiO8({1&N=|g#hyS`W?{9>i5l$aLpmQ#dqkue+Tb`yT-lolYUIG3Ct*`%#BJ8W79PGxVj^|ZMMDk>p*8bE z136!zk{%FNRI3N(c@^UAS6X;% zCX?Z4To}^l=lwX3=iRUjHd8p4v*i*e;53K%tbYQzl)5Y+BB^y%-BfS@$Zc34n-+EI zp(9;<^BVxWc{wh4P+57(j#U2mmCa}U?-w0Enmd&LOaS=zhq3&ZFx4n;dX4aKaHHen z{kAPh2?>4GTA!uB62X`AMu|!9PD>fqBz=$X{yiVBQC78Gp^M3q)$gX59wA7Ura6%3VI>)8uBxx{Ske>QPv7A;PxT5p|iNkK)^2=pTH1DT`^O<=H3mrQoXA2rU zoxbe#3j4}KBK`Zu4woA;?v4*c@&vdWuu)Y}sXfF~q+=U(sC(K;T&`p=BD>pqtF;6Y z`y%LKSo`Ie*zq6Z7^fTnDRFrmqOg4T5jX*U^<<}Ve6|Z%2h9OYg889spdF>@e2+4D zp&0jWd+eoaJ`DD}4e#OBbf-RZl3hp4{r$b;fY@*>;Q%_jnR>+x@XySjnM=X<`@0zm zO3IsUue*qFm|BN6rnDrnjEs_7M|T0DVy3pE%svVVisZMB$w~QYb$SPfr3nnbXM3)Y zkdVW1Mh1pK+mBC?=zaQ_lN~jWSnfK**f$Gi3X;dqPVO zL;uHx;xz6jq?wd^!p4u^r{`O`AG6K?>FW3Tv$c2f9!;jB1lxz7jP@`7ww)iFO4c21 zgf@&RRx1seJ57j~YQ)X}F01uqznM&ze%F+4^&x}Fa^DH}^Dg4!A5H8h#J&M{kzU8j zN?|o%E%KfI{#%lU_6t#!%G+2aP%a0-$VDcge7N7>eq2%WpC{FrTMSI zD!_ut&QesX(l|p1YN0F63jT3cBdgn1WCn8+)0;K3sMWt7=vrD@<&`1oj9K&bV#Ytb z-JrDI?b%R{FW5j8Q_%gwRuHYYh$W~ZY`!^Avy+6k>} zRrhcq0m)Y}?Abs8o=y@GHDP8`vlRJsl)~(vG+o8>5i}VqY`jE@BrSJsdeTpf4c{~w zu<$4-9b88mJ^p>*a@edVmntl@o=x++-wa`UTr_Ejk#TdfKDIhNjzwMsoLUxa?ttE& zoMr@{>Ris$*PCr7{J%n^bK4Pt_fjTYT-?s*y{O`{GN(__lZG}RVm;tz^n96G`<{l5 z!n|}O8WC0i+OIOl;l?=J1rDtXh(UwP`$+w+(b3hBSi%jD*OM|@@FqFLCQIwUm*WjS z?d`OKKwa z_y5^7PlysN3uDG&0{g%G^_;fyo5eDp|K>=gaRegxwrZJPyk;qym59Zii+DPD`Rk~; z3jf8KE>SVUEi;p|u_CQ>6VfCgj_o9f+)?Yp#D8Zr^9`)=EsDQfo`w!nt`)M*IGIzEnGkRm6H6Z$ zgiQs%P{dBk!|k{KoB=+T4X>0_SmT=X?Qewk?O z1Pw#6J?V`eL@4~^Y~clRO005QOa7XWbuE@_=aMsfc(n%*c#>AB*X;i6sWMORj*5O| z#KEGhs(^q%+;D>uOyW#gRx$;0-M3fS5I*0$U0J)3M z+-UHllENah_QT1}l=pO64jc@dV+5n^H?%jPFZga8b8oGh={7I|eLxnS}b|O!bQ;U_=rXtM@a@JUiR5<93_($BPjj zr);SnZ+*OnhsXWdO59SF#@+iJ^EG(yOGr;2?ft4o%@`~2fN`@>1`c&a==~=>ipLEs z5*7p0Gl(h?n+TWLjEXp+%nd^5(VwxpDC~(Jm?z(;F04SA5k_fPUiz+1*ByxN@ zC%sIx67!lqMLTgM!{nZy5>nLOWPi~2eY@ZWCuyesp0K;WW`HrrJGdHeLlms2khG7x zy!cRyFM~rvHw$q>>eAm9gE#2`#}bmBk{O!>nEw6O$bS9opF1u+`4co;&_oR$zY&< zBU7P%6YNT~V4l|>K+(YchIy%H{CxHu-r2@{a^w81R`VT<{vo$|n2YIYN{zkW8&(g| z@x=EHH_}Yxq5`t2nxM?|r}bq$1>&bK7wb)jLC8WBtgNWB$8$y0XUm^MmpwZav~yx3 z&8*g&t)zd8L)L@Oa@_+(V!Ts3n`7KA-?=4zpS~66Iv}+8=%odiM1UK$3F95a4=V0Y z)Rvy@0ks;p6hye7rI9@16H20 z_$`3>M|U#}Z#Pn}4;NyhqCWit1EN$bmTL8Pbvkk3g; zn)y;C=0qAZFhxVZKqLt2ujNu?$7Op|1Uj|j!;%KO#XJR(=P@Z5Xx5-@qq2nkFfQU7 zlwR^9pL5A!(cxj8t54~Sr;*%j1!%l|k&%)4`g?4Tl3;9%l#((83>wA;KtLCsd6H!0 zEu0kR=lcf+egm5qFo=Pa!lK-2Np*WLSYy$8-9tkI_iC;u(?TQcvTU#|y-R16Nu z+`>6J3~@_E#E2ygD_{yMLsp`UZW8n_xS^rf*dIs}S-1&^$!DPevu0iFc~K+r1-^Y4 zwlrEiDV6|B(su26)|#xam4qHt{p+srFYk81Rb4h45Rz{B*5xY`!8hD*I9>&qj=YW`<_)~!ow zk(d%MY;NYyduY}ZDf78T%1Qvf!yzf59L*7UXL+NJ_4;hg@@qoBfMF-j8D(twA#M~1 z$ru*PIG}mEfCt9(+EI35Idz%HgS8U2Re*&RSC-Qiix9AAC09=VgVzaoP_?8ek>V@S z*~)+*{?VU_(hTc^d2hNL3XP%9G6YlYm34USnrgAz2n3|L=S7j!#vA7u(2;ZVIGEaFG$$vfGU_yN3leU%u-s3K zr;sLDHNtf22mDc+Em1*(+B+-#Rg_4V^!8*LEDxycz4*+W1i#L+%yPKQn)W7`Q=j?f z>a^5yJsqi(z%ho3qeAw1Op7Hwp=^101o~Vb)Yk&AeW!bv_gFHo`<+s$d|T8j=3t`+luWm{gnliy zZkYF?k-%L2jR1ei_p}`R0VTN~M%(9MTJM+inw!w+0x#rXfyNV~+}sFbd2Le@9h);Mh1I&auZ89$^^rXNGzCdH(Ao zakz&FU-k*V5&|3)Ubuvq5MR{Hl#UhMjZVjqeF`HvXdqA;K3`C5+vCHo?8x>%wBY51 z?SKpG!{W0X(yO!WGX?mNy(W+fgbX+%?AuY9klQyvabDVkBO`aa&{yb$gj31~XSyE4Le`OVzIr8tT=$W4DJB5U`w8f%jcr$GOd!QCXldO2+2d z#s^y_D>r|AGvI8;FI1m)_Y<#xakVY-V?Sj=DE-XA{0V&aVkLUO#Y(_1;yHs(clEfV zU8jY>3Sw7oKoHczRprNhdm*==r@m|pe23Gehu*QfBK7)Tn-Tv2h_C4^z5*v+Pb{8` zG`8Iy-@u2e8Fkkago1w<#Aq2W=o+uJ+~kXU`I7FWPwvE2lJ6UawD|UhzOA(rPqcML&M6W_ zgpFSBNR)SBIlgAo9|NqiJk4)^eJOK(QX+g3g&`v&8=X;r2EpX;Q8;?caA9B&d>b+3 zV03-z)Xh(ywtc(yguWmsHt>hty1{HFj#*1{7HQ?P zCG{~xjAU(l)i?ki`cQo{iAEt~yWf6dj4f=nmE)YsQ8R^=y(%^Kah3w^xTcVLJbJNg zJ7FOb`8060E%d_bG?5`R#B<795EN9+NG~33;*yeVeBFlyst$<5%JYaZA3)A zg7-u&j`;8xa?9mp@2KOcJ0;y9~+1if;pU}@FCpCiHUiwFFB;KgBNYXI%*fff z4PA)I7%DJPA2JJqhtzcdLq?=@6{r{b&;Ej+9)YV+w3>y5-$|6v@?ac^#Q<{)x3Lt`cb6}$Ibp!V`Z%Q%M6^nk=|EPrjcGL^7>`@!(r zFLgU*W%-ULeJjjKUf{`N~aH{_Pd+^#bdSM{dq)dv0$=h(F^tsW=+C{o?GZLGG<) zKC=Sm2{|~vTWpsZJ>j|gknlq5GPOv+`Ep-O^@DzX_K%MbftSgcdq2saJqs+C#0yyT z|F#;VJ?v}^dp)odGgTZq_8%NJuS7Z7Md;%@p3Zl{gcPQQKj*8g95)Gb1%Ziep&`T< zY`{*aBVCTuNe_p-TZL22>8NE9=(T+QG%COGjclnA9N1TxYUr7ju(n39s8H+q`YHo2 zq+Y8(r6g4QuON&_R9QA#zEj_>_s-~SxdsgVX3YH1lXi9GdfNK@vZI>A z$>xB@JYz0FSPv@@)|Z2rYrXFjf3_P_o2`&0XJ@!thmSyRF$9OS#gh_R#sGlc#_A(K@>EJ$Gk$~D*V z9akB$?(R5oLXVm?1jv-dj;(PA4&RT4ESpl=Lp=qEoV(5kI*VGvchqYR5+?v8R8*<# z->p{OWf%T1%Gzu+njyn)`{k9Fo5R7wx9+~4L2NsH74-tTt~U$eD^bF-_fMf3{ckQn z)KFlhw)aR!!WVEkHv?7`7$IN*iqZsh@_jf-zywUSl>ZZgmM2_1cR;>A(y_bUZ=hVg zz=DG#@E2|TGi9;$N-%A?5UXCJomRo7rdMA19Xuew3XC4+kro*>X+!X?$@^$1?M(PR zr~c4DyjB~f8uov177B6?ZZD>3 zw~sn@ucAa4KZP+RH)n56cjj6?g@4&Hf;R`wbAoj^HGewD$}bYXJL$hk;Or7dGS}u` z=VT3=?XWA`AgIVjTrSZt(Df#G6*IJI!rLO{Iln==gSF5Ec-YIWtO6%qW0|#$$?!3smH4w1JgDI?sv>*fy)ohU@c19 zwBC!@p$nvu3q=mcFYW?7IkwGL+p;b6i+%@RZrxqrn%^Y}!{F;Z1X(4R_Q=i>TV9Q` zGf5`TNa)xIvZkVPQ=(M31MbP0i~wDTwWVr#e`lQw!{kkNq?qSgZNGUw{!l48`^U5? zw|iUD%77|P!&hIg9M0&sHw_~ubVhLNgoL`h=h$yk_7t&UzNzYJtt}PJ2pv^~Q{hps z;}R)I2MaNhgJGzw$DqlQR~muTnR$Ph7VpkUSiABxf8V>4$#moXeibSuKKx;QI!xJM zGL}d;M#xp|2=0+gE+ws`#4M3(o*7zM?tH2Hpi6EylcixOI!WucB?Fw!naJHx$jZGJ z=y_SzQA>SOD(nnnjT@Qz{l2D)$N*~XQKL$w*{9JbSp6*hhwrT1{?B{hSno=OA7*4K)dU%221>I&K; z3ZY{cN@YPsZVj&;kfw^o3(jk)$Qt+{7>G*YQVei24WJ4?qxrwe`JCbuekYeg0Pcnn zi#~YpyoS+X8t}j;kKOzpukmQ35_OMN?;jg?bY!E;Kpsf@t*kwOx_rC!Qs@&ZYuS-q0PcmwYjy-qo0-6lV+A_^n(;gQyZ52Vk zl%PwyKRtYi&nyCjG76Y{miPm!Q~%NFbKYbF&st=(=~j>l$2;A&O(&0)Iczt1;_paS z`(Y!>LTV4Hl0$iG(32MidTMb}7!VSV(=DmG>jhp%2Zp^3-K%K22?)NGP}~>D$3p+I zRhZyZ?x;&v*P7Z?QiWfogP+03nvFH%Pa%-GhH>sQzUekN7SjxEL0Q5^?;55|l;gWcKn6dHh0 zFCuC}IqO5j8GrP$;9G&FjoBtF&RR3N3sm&sKdC$OyT=aFjgeCVK3Nt`> z#m7__!Zn@+23t?)vfiuE%c|F060hp9o2{z-UYVj>`9_!=CO-bx|x|`MV3rWLoILo)IR|K6~hw1Y6ZTMlH0dkM0+uVqkhrV>t}gn52Eno_56~2|3=HFR4n$8C3*J~ z$(|Q-rOVpx1G;qA{_f}49mmb<4TZkYcIWR5p8enTn$i~8_0}39`x^)iZTQ5UidgDzTI#Kc8Ely`0`FWp>$I_)vC}n0zQgYj7mP8#jsG4=%_~h zl$r_^i)i{N2A_sO(?jxHW8eUTjh$Y@an7iYkw1%^g4EL+)?|litXb0BCosN)a!!={KOQsAs_JNOJ)(`Dy{nYUGUq`R4JA$b)JUy59o)M#)NX{H*|P!0B++9`=`Q z3|RRlR@NJ;CBaD*%i)BHU|4i2`RvP|7$MJ|4;6Xw&cmB@&I)1`){4Xi@_&yWmNR$% z{C_M2x7A#tf!|Ap8oghsOIGvX&=S0=6H0*mw_Bd&p)sXD_Mao;47Gl=lu}Ny*H1?= z?wv%LTvonl4MC31(h)G#PsHf?BnU-Xu+43d$CSPh(#m@QWZoC3lxkY{#sbjtZJf+2 zgWx0wctZQZw^uNbp-FbDx?W* zo3UMQxxz_@j}X404CRP6jutQZx}Wa9OYnZlJs9klMe@7zVzWNy&y-HOqm>pok_=J# zL2lTt|NjgM{(s^w_RCr}H?4y2ZN7|c9&M6^QlyAyazDrUkltx!d){q=zZ86lguz*P z@$h)BzjP}GEL6JSgWYfNI*OI|{u1?GEGZM=qWy54*N7Yqe`Y(~e6?D}V|2FgUWUk{ z97y#uRc!cgiT}@4e+Oe)uXRRTY&5Ir>S8qbhM=roByiZxB`({nr)JqNChOceDyS0i zhc%00=EDWhA?S~~W7G8KlVKP%=ojEX{_5Q?!LZmRXOfkM7jiI6#QJ}Y{XZ9ps4S8# z4nYa96!V=g91kg;+Wh~2W!=AI(SUDc7RPiVFY|vb^ZyJ(=KOpX*K|aY&f*YSC^j;E zM3GIwu=J83WBh;aAAEqv_1}u%P?2gW5d7L&R0;K;F zA66;qR!scBfy(eKb*j8lcHqFIe}G)JorZh=l$AraVq3y~L_{p1b<4J)v0=5{l)Yw# zt+7r0gJud?i)^qv5F;@<>WYx#Oh{-n*~Y)MEAyGVa>NMCUkKuU{5C94BzzW5?#+3J z+P#t#uB$fgkM;GNC})AL^Jk z8bUvM@q9IiZhX18So7BMV?#!=4p!L~&n|}d>NQYyqnv~weSVQ zsp^vD@Uz8jqr{+}$RR1H%knF+Fw|gAbx2A$KCmS7hCjeaJUIsHs)x`Ed9bi7A#I;g z{d}0-*P#(|ECPFz6p)UAe zL>I6^lY46={1#qfPO5hVKWYpk_@k<*pGZmoVGp%azfaor|DGNV((>64+}}7iWI8!} zZflL7{@m#96$n$Zf;r6=3VoY_K0r;K@=X9~xR6F#-+2pe?S5o^SMnY0V~J4j+4HhJ zb6QCUUR#9r7m7aqIWJvRjz$@WMfjF88ZM1jR*Z^?YpR$F7I;P4XPXDfn8bR`>hemJ zQ(OtzlYJ&}T7Cgx*e_(q{5^UGZ) zO7Foi22uv`+3mF38k8S8XH z%*S^B_HSSzA^+WHiAmiFJi^QXUY`WtM+-Vz??jZ3nmg-BE&Sc@cGjb&W6K10y-3Kd z2R|g_>GjIUH8h43vfsS6gKv4JEUhGQ?v?A>Htmch8L<*Q;uH zWMu!LYv?tS*EjW|@jvy8AS7~s@fDZ%1R_yZK2=qCm6bV4l8HYb_tX%m0W)mo9SCsv zHxsjToX!7{8`c{07zYxSGI=6U$)!HP|9tgg7o2V~h9gAMMRg^c{NBa>C2@0S=Rin^ z`_JZ9T73SO#iA`!ka^fa{fjGrV1erXQjN+IX-|{z@?Z8VSSloY+cG%4wC(2< z3^9+_1q@~uy=<+E!E&HTO~_}=7a8sv2Nf(()26g2#ERUgW*exh^Y+rghtW;sn_r|+P3 ziHx>HMdbr*jQEt4n5jSXpRt8BFr@Y!TZ<7fCAM39OZZ+w9*&367{CQJ32p!P{fw10eAG78Hjr z6E;#9MA?GYDP?d!er=B=80njC^>{je&0abBlsu#Lp}#>%o+u^m-b-toG5(zv?8wx* z@$#>Gt=i&~ITyzli%Xc#^$uJaTKD`Uc+jeLs_zc36k{hf_sD3nt}_RIp8DUF-zO9u zbUtS<=h`i)H9)-(gubTHtQTl_p-IZai27_BArLc9h=c?fa%Cp?;fE&Z?G zb}KwOTHx@NS>rHMvPd!}JSL{5E;DT5ghEItud`DC>_W-dF2YDuWd3le*mr}SEdhsC z*e!hN|1~?vRI6cD7Y1Aiqj`@8zjNPX_PA?Dw3YP)@jFV&bGm+=xk_(_ac6U z!PzROlrj>aPYRf5!8>#WWTAH!WWi^W$V!rg<79V@=Pp_k+CSo{x4pA2gu8NBWb~}0 zQCPl{o%90bk1pk|_?E?pQCp!#$OC8=Vz`@uq;ZTt24vuWW3SR`B*EDzb@a=cwn)}0pMtE$*Zubc? z7>)HfTBDY0g8k4_bC9UjX8;+|KJ9#daqZ}mH|V?ik9ND+im%oA>||tQ6bO|wf~>UD z*I9Sd>1oE@uhbzkV>mRjlel3)5VWz5Djn&@xDP_^*?qbQqq)`&uS}DlunofHZ-t=N zwZlRk-F$z1^oeX9Q=4(3&Vkl! zbVfA*wI>K_g_tknBxW`r2KtKOu0E1PDHUF!!wcn3*Azc?G+~ft+~qE-f9YT&j^Bg>MTAt%#*3%FEBh0O!Z^ zH4f|5_#5u6U^2+BY4F5rr0@43Lb&0dWP}`|C+LM5d!_l6a5d}01(G48=exR}w<13Y zyjdC%T~0TYi~8$UW3yk}3{b_w&dTQcypec|eqRVAtnd+cG~qr8aSdi?FKE@)ge4b= zf&CT;roq9-&a4dT#iob_fCn7its9)!sJdm{Irfl~iL}P9D|_vnqx!J5u*SB|mBT}#JKGlgtf^seqH+M- z)YD!Cgw0P-t=$v}(Ca|M#Fa5rR{nRDbkfsrY_KvxDFE64@2qKwd@iS8zRcM8xY5jZ z7l+M!@h#hXE|yB6OvE)R0;o3D=-nMz(9>MkvtsyeMhBOLC2=S!Dmik_e5{A~B%kx-(FbH%$B1#jyo z-94ZSPQmAvZmxE$pTA2q3DihC9SBfq9mF{Ra-v3%QaL22OCm9HU%bS4L~yGN2c#B{ zVKo;(dDF(VV;>4#nVy^vmZwXLRTUb)YL8YY#OjN1^tjc9wrsgoWX}^^w>^Om!uVdr zgtV}KS=D7ZBdZb?_7bFsjUO_Xthd@zkYkNeJOqfq)r!ALaKd*>6JzIv^$$LV0wQ*u_}WrWU%1jjfSYW( zr{Uy1s4DJYPOUin)%d_sw7#Xx*~m!FcB$Sfa-b3x46Oq@4ew2c5P3e$qpQXe6)b;? z8bGyRX#*4EyWbu=RIb1bTE3Hy334(QJ?5tp3p`O6RkcGfb?3d#8ds;;cId>BG+0z$ z-}mtFS%48i2qI#UGajuL8?Ws6BN)rsytdvrtbdLmk6V(LS@4)$rO+xf4u00Ef1zJV zd^FM{l1`!Z@~lsY)HB~XvOv?I;1{uz_u0W1n7r?BVw#p3>>;g{ct&=u(=#F=qY2Z0 zwdAk|Az2QGN*h`hLP-}LO3ORS!ta1M-)jjSMHqaNZ}A^e@xLNr|AAc&-DV$N#>l9s zHp63SEJl4zQSDXo7)X!Y#7d#e(p`I1L8opZfrH15tA8hDc~rc-8Q?+?4KNv+7F>c! zjmzsuVYA+B_|N}qN<3M(%~}#Z)k6tD&(Zl#W&6dP>8j+%M2#Mq`BFFLvRc+!BENq! z&({Wj!VM%N8_syD%BY&Fsl6e!hgQo2!sr-!qk%$?&bv*oMXw_&zR<%)t1Zt4G-!4U zd3v%3vgBgTgKmk*oF{09AiLcFB$v56BozXKmh6(B*c~lbVlo|xs(C4e%W@PVg5#kn ze)g>vW~XZZ^ih`U;Ke*!3III)uS4+ZpZ#7O=`QGI8{^!2XHTsm9k0dtUkvLXlzHGZ zr7zr|(V1hVNa{v3`qTQlA`a;rBTo1tyA}H5tXxx0bpE9B8w75~6HmyBI%P`wj8EsZ zjUL^Y$!}A%Y)qxr=&ap7VQmt*NxxmLH_`9-yb*>-gm}MgxqT6rkeq9>TEW}Mixgl~ zRik8Jh^Deo07ok@kjlylU=N#PN+;yw!GDO^h1@J8IT2`v?FdY!A@W$2`BVC@rlp2K zKUpe>qAQpH1~1JpDoS`yallk8Uk*`%hEhP-TqX)#7>JV>9qCi=thHxRaK`5zl28nR zA_}#s|6UvdIMd~JrZMn+p#j<^NrJ#q~eX7L|PRFxc4k0`r&w*Iu8IpGz7~z}Et?DOmoXpSrry zr55g+p{&V3S9ElA{84Vhcq~^83E00kNmxTeYEOT!!@?MJTfzD5?yQrAvb)7!N+~k^ zC-yuvOiZOcDzmu^(v$ zXi?YaMrXDnQ3Z_km><*FLIfqC^f%m1O1!VdF}#y`zkVFH)EFPH_8kk6l@Jncq#hj2 zaRGmkkX!7uBTg;!`~QWrjx90)9uNMFhCg^mjYwnn-SpM}xQde3%91U+#CAF<*|JE( zAu@4?C(Cj^3Uorqs5v#c1&Yx>8@CLv)zGm78llN3Dl(xH8C4~T%Q_mB#CQqO^_?A13!w8ZV|GOjQnK%A@Gagu8>rb zXtMX6rjh8vU%0#7<90<&O&n2}egUq1K?VeHI(cLKQR6Ud$#linKE?E)GMb%RFZBl`=yH zR4XhPpN}ILHzM}=0a|N!!H*PhDEaljxd4_T3lF^$D_J*Q2B+|}(Kk?NKI%rI*|uZB zBS@^fyIu0`_V{iBAKXt<>x01^8Z5i9R~hyjDz~?yI+^3nCmbWT(=oiRPPX!0nZ`n( zH)78%7hUf;i4oA$5VqbRIKRvO;2HXGMdSLsH!7zO&P5UtVVR>7+2SDe**NRfsw+nQ zyN(Cz2bNzGyTj2ZRXq)lC^knMHLD7llJ=JeX!%;9}IhNBk2?}rY#ccOM^O>*edoaA7)96h{{a<8#WmHvbySAc8r*w)SAp+73(jq0@-QCh4-QC@dbf?lC z(%s$NtnXp(_dVwuDNS#%@(2Y5lQ8&aB_upF|^ z90e|#{)0xz+>e1VHtdmqZ?rS+{KGEvF}YbPB05$x6w5T8&r!mmcx#sp*S%lPB)z*{ z?q2io_4YTVxt?xbvk~;PIW2HA6Z{vZ4V01M;s*x;xVVgUZ)S-I< zYrR1FHiEXmP)Sv>_HKE^EwRS4Ld06K4!QaMuB>EWU-&#UxmWav-{0|N1G*q#qeOlD zO9`(~JKqzINKAibCD|Rfkxv{JYJXTY-TR7BK5Z789_!ytN_=ynfW>FQuUFS!eDI}7 zi(4$s)2~}nJG}*V&!_XQPQ&YnqEy={@^aZRy0B&-8jQ|i#v-xDtc$QeHFx+rn+1e-u>L@quUN<b28rXmj9*p@haVA11HZcmUaQI-KO3Mng;6=q3U4tccefgmV4k?Aj#eE z&W44zbV2YBF^rL&g&`j0yC}S9v&WP~=eCQH5>%r#d|znEfk1x!+he$Z$+3yXN|i6vu!PkgNZ#DJ`P$B|@TT^*gZ&%D8W)y73UQvx^-+m( z)f*QNrdIo^X*I|I@}`4*Z-P3K;UyB1CQ>At8t|GBUlrz3&DM7%8?9 z0gLa`{gpnTADCn~2h1OC^+tB+&ww` zzgU!`?b|c}y4pKd*7#(L#y}!&+SOKE#&5jQ+sC@+-JyJGi11^5zrI=|@JBuP5%Z{u zE+Rw)O53uz9`Lsuexhw5i}Y{3;jIk9x9+-m;|GVjn8?G;v%UKPvZ`Y5ACMnwGSeGd-WGj^>#15%XR#KL55A7&yIrD2+jWCQd@gF zWnXyo@|eB(erNq)cQHE5*xF~`SMP99Sb?9|Vn=~*dFUga7{*v+R7bMn`E({32f2`( zNoeEy)ch&wu_}kk;?H4I5_@S^X;`2r)_o-Q7zJ!r26;g<7W~H2D)&VW^gW6cJIEg* zOzFtTi^c~VJ9WcF(F;v9V>E*IGH5~Sw--k?G-J{%43$?^`l{}+?E)8HE+x=1Xvi{5 z(K7?S=%cl{(fDZ1O}Kpd_6C_~Z6G@{ON@KvC}S6$1hMoBya#Vgv_s|H5>{MglM<$7 zaHaXHx3qj?eDK8DXj-9q4XY%7X4*V>vDNL+iWSSc4G?;EnRGTVv;$(2EXW?dyOz?N zi3bic{AY@%^n=dyU;aNC_4wZst~HJE8xz@4+6ubunCRQU z*2km|NxcuuuBS)XdfQFX?{!2?<`U7a)duu0yG~m;i=fcDZl}Wy28xN#P7&I&g~)8K zA`DW9-_g7dbR%9HYTbOOKDu2v`>L)&tI}S%iJ%m_{-sV>rcRrY#!QeV@y#q{g(bU_5eFLK1?Jg8gH^yFa$AT_=1a^!`cy??GKhX@v zv%P{6{T%tEm(c25;9=NEc05brh;`*=6K(`U^w?a2C{ zfdjgnuFb5FwoQxWDnnLcw+FO)6e6}&{zO!-4Zlzvn<3P{f0N`?t~5PLazQgRxAt(U zB^j(ri17pR7LEZOtG{>c0BcS%Klb-R|w$Y_~@~uhdlrMZwFsYW@{mudT0p5orc)(vp-WxrxEDjD<`Qol|7k?EZ%&D zgS$r|SZU0hj!sKM{vszwrpjWeGpt#5y55@Zls&DMWiBmz86m@R=Wx-`|4zmM**x&$ z7fdL5Sd5oNpWjnptQ%6cTLq%F$`o}Q>MCK5c1@0$lep2-yslvRd(2?t^_Rlh+FH-h zaQ)P=T;-Q1+XvuDw?69mg-|*Bs+jk+FCGfYY`OJY)o>E5a8=kXQ-D9E55X*xANg};99xV zeqA%3l>shT7NLo`nUtBD770vdcy6gPF z+^g&PDc2Y4W&VT;LtLGmEVijt6kzE zst&%&3aB+p z+jy%w?HIiUGBK6TK9<1j} zvZjXCmV2rbb>!YY6{3s{_Nv%Riwx<3gcitb1afBCirgfIx!gV<_s!9?{K1n7CFA>> z{{pfCoOMQk9&41#;>^Yt)c7dBfgQ{8skFTDs~!)BJMoWi|H8q+QN4!!`j$@})h-t{ zs}#EYx|T&w0a{Z?`lUVJbmLUlo4ueQ+Mo=b4#`;LKQBCeF7)nNj)|iY;mC3X`p+-l ziW&@aUoSB_^gC^(Y=8g${dcOvQec-svD!zW;)-8+@&jxs@hzLJ+6lS_w)$W|mbmaoK6I2w%nGAq6zOkZ0sN z``!1UWIjgdsTw1#Zue}CCo9qeu^0`?byJ>WTEEvXqbAh_@-1(IK0jsjX38Pmz{pt% z27Nv3)uhJ8(iU$|1TvmSUP+R^zy4bI&0KfN;z4lL{bRTygvKvJ3C*<&)@MFWro8_f zF^2UdjXJ47SZL-`G?*jDA`^e|{abJ_pL`)DweCcLbb>hff(2_)kBN(4~0U^@&dAY&H?{E18$zOcnJ@)jOON$93m>aLp}D79qj6{>AlN*WB&!R{4JHeN=-_P z=TTdCeJA~nJ;zhsJ;D~U%M5XOftpXY$qzv>q)(&GDvTm36X@^Y$gT@QmW*OMB$#SG z&ca|24R%Pco}jS5WJ(VYIob*`b9K2`Iku|%#1K(}bre0h#nPLB*tj^I3MD*Z@q!Opo(cmO5qIb| zQ@nF)C!ca+Ba`}b1u#y+)4pRsbV^j@*Cbv*e{-cwX`IPD4>Eo`Nf?`JX%&_B6ItOYSS7jq>3va23B&Pj`rwnIBe@ZT~*hL_*WWxo23 z!41DsLYXfj^FA!dW`Q=IH_96-$b-!{i_-sx{A_*FR`^;1je=9&KNlW=6@PBX!Yv~+ zh9CA+pS0KB)&k_=7gn>8sk8o-!6tTk)z-D<*S$!7 zB}b)s3`M6HVZY(nIw4^0cN!GJ{DZ^NVaumz@cZMw6B4X4q zTQY4XBa&YF<84bHh9}ichpfrT%gCr5gs&MPg|1B;4Y6VoUAn`W{QJ~3c+yeMwQZ~F zj%WUykMNGvOlydJv$0wEk=;iIx8{p#o|Hv-vA5{2(Q;oMNy@>L)6V;0qgP9WG|zt` zdOj;^NlR-2_t)E&!lh9fv@W!N7c7!=?qaj$3U5VCx7glNbthmA&~Q*W0Oc7xbtB$* zb*;Ja5;ZGyV2}4ZUS=Y4I=W11Ci9P(t1Q-`x45`e$~FHpyPUvO<8-{elM5|>(QDiA z!i3Oz?5|(Cvr6!EgVVXe-|_Kzi5C89`DOPOe!R@tmk2deXjn+_Dohl0zW>~4R6@~j zHVn2zF5;jSL!#0ZVoczsC zP`I<0&rQ6y*L2#9Wg|cq4#D{YOg5F99qFGt^E2hzHuSJ#@N)9GB@exmtjzL4Wvr+K z1QTob2XPTI2OMg&hTa1vJcpDOL37cnI4bX7jLoegNQ_%GU_4#1imYH@(bN)E1yj(` zNmZJNJ>TB{-fM@GGm-mGjkn=Q8{5S1-e*ytE;j0Z#*>!E6cK`+ud`^kIQ~}GnWMYZ zcME*Np3&H6OP`kjv`4mcm*hGXbtbGr(`}UIeAhk8X8S4r292N(qh@QyfAXi zT^2(BDqnDR7gJc?rikjs)>ftNwj}Tmk#KkCxxIw}Rzg5!#&qa}MW+!WnZFLskFmw7 z|GDSMET!4?^#Nd?*|~Ja5aN@@Kz#EmQ(N1|$Is?7Z#XymY|lXgJUl$8gF%lw?f$4K z92x;Dqv+!sZ^-)NT32viZ?BQfZ>R#a@)tN9c9bL}Bqf?{Y3`4AH8N#+379DA-K3fX zow;7aS|2`fL~2kx#UkXVNkB@SN8xX*=9~suA>QGJ>BD2K{S)v!>q~_w=eBd^%}<@P zofA=GQnq^Mm#Sq|j$I>Wi>e_#dK4sXFY+Z?HPOkN-&9L@>rdt>G14jce z#Y{%_ zOK6ODSQ(lAlFxTKm@U>F)(sP&BPr^hGM{(c|D|_zOs9J$g#8z|8FAFHCNpCxCZp?A zR6mTh-gfiD>HRaS0`P!ez#gKK9E#d&e;;WXH9PUJ-67Uxy|vCjA75W-sp zwSW0W*uMJBhZ0V=`-{_XC^l`_jwo(5=XG>2;f4Jkay?_&JXRk+u%}pUs`>qD_KshA zj4NyrLB-o)V^_ZK*KKZSf>HeB=p{Cea>*=-%bi(SC%?j*Hv0NbPk-zJZcozPMISl% zI?r~oZOdBvoBZ{op74V`S<6vN8bz9G+$zQ33Y|7B#QN~t+F5?uMrFvJ3wKkXJ&PN3 zFUN>Fb=hSF2Yp^lpMzUFWJ*&q-Lv}py}cSwI(#No0;B9GHdY2vuYmj{p1zm2H{f5H z%~s-i|M{6kX(zVRy~OP@-y_Q6tRgamRMhg9NxxBKy|zL@BX<*X^SL)XQ=(v2y95@a zFpI^BYzt~p)_=CK&QnwZ^ts<7jkUU1)xE>RH>9Hrj9)74-z0Unc3XDMAqZvMH=3<89v`ENwgT8Z^mi-W>~QVz zv2*Hd9OiBj(rK5aI(HqcC4(Ba+`277T3iW^)gVOau8Qr2q^#w3vl{J^z*k>SNi}bn zUR&e}mzmC@E_rWOl#8ckHs1sht}EzmzN`i(9#-`UFg|$+)KZQuX{5p`prp9F*i}8e zZ9@=}4uiF@lk?LBIh|2Dl~vQ9_o|n6F0D%*WZpo-J#W|nwF5?GGa_441Ws1KZHA=_m!)!WLZ84fvDe%j&zjx#*RVp1q$2s_zkf0B(c4=95 zv|jar7u~(v^^FoHB8BA2MS$wfNi?B&eHMlDm!S~@hla2X4Q~2r*;JQF?-c8PZ_hUS ztEz3+xc+-+LbTSe|wFbvs$>~E!Hr(7U`MSoL9P;gK zT=qrx$+IEE%uR6!?phtgE!~@?nT-Q|;EHnT+}qPmR@KI*M|BRTjlHml=rq#Hmb84P zx6k8$EtKkVFwL!%D^A^sNHQ7Tjt2$7w>~*zVyiQSh|z_vPis;`Wz)@7g1$sw5zYUc z2Efr^`Om=guU&zC)d2*$=HD|Fx`9DK?*VNNfOeo&=RLmL3CMJ@u|nF~B#_$?567d1 zZ9I7g9i8{Hv$3K=ZZJ+afA$-Wy6*$S{{B|{0%2ktaKA|d=tlM2?c~mPYT5Ni;%WVdp#gxtj&hbY=B3*Lbo{N=7DIWcfF4k!M*VX;Ir{5kmv+!WlI=qto z-gw~$k%ZI>cNgGs@wDvKhb2U9=d=u8b6h#+8RS?2vbLwh1YCfbaGXv%TgQJf&|z zCr35tazF}hil3B4YSx>BJTL5Lz@3Q)xGx^$G|aJ;GMtw_Mi)-YPCQIMglJAe6P6l(Ls=Psj;`C+2m=~) z3Pr~(_`UmaBQS?{A`@fcw$O{HGd!fY!@19Oi__98AhCl@Pl9H`)S2qK0lPZJf`5fk z@OhNGk++G`lJBrhe9Y`_UC++t^-}eOuxCTrg8LhPah`n6m>PrkWPuAJ`M_M$$Bt{K z{&vSe_SS8jWkDB)h`zF;NtbbcL3pjHM`y?Rr&3k_okx^!_Rbz=J+{xQni#OE$)-kp%!2ux?| z5=}n2biOC|imhXO)1)^=Tb5>J*hAX_<4pV#+8ISNE%2?Wrc~1yI}v;c5TjrfFDwZ{ zb89=S;C>O}P{dEE7gO_COD<(TF2^lNfS3=D5e;lV$rVfgJ=pYDJcBGuLng-aukZ%Umwo5a=Zrk`Fw?s|R-Gv? z{F&Nhqk6dz6X6I7LlYH>>09db(q4-5N)Ma+!pZYUjML5LrrVYAV?c>A_DOtq_%gf?4!3)=qy77*SGaE9 z*feQ9yu(Kh2iWZi6?ON_hI&3y`o&D7DT%HQ<9gV19F8#Dr+(U!k`y-Zn2?f}2ZpUh zJ^u8)J}PnEI!s=9na*5BJSau;p+!EgFBOGq<HB+SZ7%<8xY<-cw$jQqaZyNCpN&OpcjABT1Z95(G<7|I~cUno9u~~#y zht;*a=&v{}u>({g{bUbL!RyqxKdN+)a7%a*hAE;>QAJ3qblb>UVX}q6zLTM~j0u@S zO-zgGbOq~?Ybobqx;#JiumLDqCvk={s=A}n&};>l0vp3jN97%Ch8ta+wzlwRotFe9 zC69wjjbMu7X$^LT5wee3JR?`(Q3uOSpN0#VQ7Trp+Nv%s-{PcUh00TFcU)DPJTj@0 z?wJX5PTL>t`fD1(^F5a8-EC;r-_}q#THd(Q9!O@-msOooEMItu2+7JbrNcWujQccO zFx4RpZ3^16e<#E&pruI4hRSV*s%a)rcVf3-E_hWJF?AuB5(;Osv31n5POCloTMuJ% zG%hI1ssoAj{ zPx+cSZ}}>HkA%Q)@tj!L?VM(Kuo#!f{w4>OkUl>oWBQz~{9xc!ijyQ$V{hcby_(|P z(>4;%(++NHFI|rzA_)Ukbo8)&w!)7>Zd1+m;kIen5gp5AP zkV~$XW!KbeUXMtBO*)|(;ZagkvO3!OgsM9`IPvvSp|tLn5uv+q)4uUfBj9~u^Tg~} z-=K_;WmRUNPn~ciFAUqK-Uruwa9|!c?Waj;Ia7E#KQIh&zUdUM(=?Qg9dB2a_EeDe z%GaVx+JjEVA6gf-5Gj+v6<# zZGkUs=+rgPrvF|tRSLQcrE6c{)KPkY2cxcsxY^1@8dItY3hOQTYwICqB9_NY-S*CRajxHi zBw`*;ZD$YpOSM{KoLfUfXIJ+W?%n9V9oPLq6|=C8TxjFXiuZ?%ol$%}34;G(9E2kB zqfqhK>Z0s^?J|hI`Z)0Yqu2`fR3Ak&)xuhHpxlh1?-*e27$`XM@h2BAUUc}t@u}I? z_)F*C)~^bqNK4}xO2N0U8gYtoPvgTG1>SrE6d(0QyTWt#vS|Tu64*}n;5lwSmt48h zrQg#zizz%4{}&gtnnBICM=qs z4tW%~K6-kpfzs12w_evfprg^v*K-1eIJ-_eE2UmbVAs!3+1 z$ifcYkTHq)b%zO4=$vp$WO|x%y^5Lp<*^quvPDBze+kUu#@5H?hswxYv4Ox~*?IAM zCZk~?+cj#U@O(by5H}4vg<#)#i>6Qzp>Yv#54^<_SdXoNiFt5dk&XU;0VkvBC+BY5 zgX%phsUfoEec(c$Z#fh7Q8+vDohm9f45G=|(@5G5pZw~S>aU&4lc{8v`u$P_<Q70Xcl(%51c zdqOtX!+FR^N!M@cq#uQlX*}kXP`Gfs#&LrNEFGuDznf(=9Dbqop#0Y4H7&3#3QsDoteN0A%E?tgr=p#rcebMb;GY-p^Kg#`f&aW)mMsho(=YiwGpS2>lCYK{G;H1O zunrxTY*w7G0qco|={XTqrYGpHW#f$2Yd*r}>zB?rY*sHcT~DEnie7*EXJ`p-Pk!Wk z@hf$b+J}v(w0_M-@Uh`yZDKj>niZTR2vDUFKA7BT+$ zG^z7rIxoO+5tKn$Sy?Y|+|Q_7?}v)sOe$-dt=7(4_N>}5xXyh3aX8oIPwY0xD8)Op z^2a+#z6>?XtrwI%2lb~I*x0YW%dCw^u|x9nZ*->Nm$>eEU*mhLKC-FUyY^&<$1pjm zrb|^K|2r_Pt9P+@OEiW?+8)|TQFsrI6)qjCX!hs0H82(~B1=jPt9%&;8ZLPw>C#dAKJmDWa zT7s(2 zi)o)v4uGB0*_o%SJ;cBw6<&HQvc_ULg3RkpcB^pW%U4qG*w_MiSuC?03=C3x=1P{l z6#9!OELYPtkMcWLp<5E)xqPB>lQQ_*wQXy|YCE*%t7!$nw7sd>mS|?${*+~^%=^Xm zz+iHX_4c@bdA7C;C%h10@$Wt3+5>e0!`G3&9^#vtTbzy%&+&4o75xzi(biS|4~CUe z_J!McN{k*01seBY6osemsRf+{Kh&&f23&?Col-7fTcgk(AFeTwLWf{goWZCB7y6CN zb_Z9@aSVr{1BK>mhP+Fj zRF_8P6mFpkC@(nDLu5;=i0)>rP8M?B&3eLdQsP?je}ics^LF znpl3L7MF5OOOh8cg`i?2F(?mqj4b=XWY%!w@jOuhaDps2*d)4De$48Vbu^mt*1J3u zGctn3)?mUdo)i{#=0M?QN-k4$8!CYz(gLPWg9+BW2$1u+)iK{UBkL!(t_KSZ(Mb4^ zFVajJch?A9r#I2|LP|-jyn^T7n>~2-3$f|1`(NTLY+@WO_HG}4TKm(4;Iz?|jBE7k zV@cHGuE*`{DD-|Kq*8BPvw?hEsj9KErrGuSvAKmJYIU7=fI^w2U$&4C?A|;rFl&W^|tCR&GhbYt6Dg zF6T+DPfIrNwo~O7zdYy<>mCQyt=LhlYFKbGQH&{C=g?^l{jsBYI4aeZSLdiDiFj_9!dLQivY%I^wn=Xwxiv=pM(a%O zd%5A;;&__1r~NqVt&7c606RAP3Jbj+HA|N({13$3(SC#% z(##ZL#6P&!E44Jj^@tI#ZvA0(bydw_@lsD;zry*_1dCSftFeZC4Ce<%dmFVvL$$4nEOxMAI zHtiY~J72k&@LxVYL3>z= zMShAgMpiieTlcHFij`dt#_DDD6m+#3S2O)&h5c0HNv}`x_sn@0r0$Q5gU+O$J#@pr zWw?!`e`s-LuG{2-^LyY^^9)hhccJy8%1K$HDRZkRFpj#|Lf1 zkVJ*U=yW@s-N)Zy73AOnH$dCR%jqhuKTudSDxH=313SQN4rH>dKWCebq!2h??#Wk4 z?jh4Y+|8Gn%{BW1ds#F>!q6UdzpX`oQ4iFKR~N$}2K%+dJ;de4l^`xnjKY4j$Vo5l^ty)?rx;G!)Ne4SLSg4Gs>_gL{!omSa_+`6#ZseSFZk(*=fZ_j?58#aXHRO^LY9*i`t%0HcUUi(P7|7?;9KZp4Q zWxul<85z{@vggpFSn(rvHr040UN@N2MMZZP#r!Bg?I;7x5 zy5BV$7ESJq&L8yO=kvP?BHkPM)ya=#Br*{+brWwStB;JHHclWedJ4*Y0|cHQb2s zbS!FtgX+_`kH9lOmpX2_yL&u}Gk&|jbL4t6i~V5D$KV#?e@7i81HmqBEN#KXxa6c> zzQ?xAS?tJt?0BU)ZURUM&B(;_%ZyKh!FHiorM}XDUc}sXP`!lqhO!Oqkj3b!U=1i`lZT z>Oi|)%;RpBUc-v_d#12 zqW41%^}s=3}d{-x9G442+`E_fqVB`3eV`; zSu6FNr1B>T8dG-uGnd5xr;reKe~mUb^M{fGU45HK|LSt0v`wGAYp&NeEJ*QdApVzb8h zorE2vCbdm$wJ|_?D*l_@e08j-tVjlhG%isai7Xbi&$k5USRiWoBiih!~7)RgX(XQ#WAA zkAKm_DS8HykEgTFp=n-RICH?YQwd!H>(%nal z)F!ZBUJbCbV=xVUZ9856`gCW$H7v*GaqvP1$=6J+H370E$hfta=_4oQ>^!p*7=Siy zbz7wBXj5|^FexyxwpgOtgcaQb!pepo>RoX)A-LU|`RW6jjiXfOOa#{%LdsN`nc5(+ zK)Y_9I=#c6#-Z5Q^Skn7{JlZdZGe2y+z|q<1d+KmTV-iC%=;u;<>I1#pye0SkwJg% zTmq|bKn}FsPIZuxk!c5h-u>ackDV7y$8p6CaVTHp+Xi4rNao72xjo8Su2hu!l>(42 z+-U@DInI!@nQ-uwG&oHf;0#lR%0e`9@~+6(*y_=Ga+&uW!-3AvXqwWiO{25Lp$*!Z zw087sV?NUQp7J(*X*DYgr{USz+vD;3u{Oe@_3?Nmr*f$D zKf>|7Y?5sBNKG;ghpU%bWQPmIxNUnpOHNK7NDV^Xzu$OW>SRWXdrY&IO{cjX=+J+n zd_ZP^ynQD!ygeB5*-m~SfjoW08)eS%FjPwUV!|+{Na4Ng8wRJX4=K|lL0R(i$7taq zud_=N+vE2kp>*FRMZO!sHZEh^LU^&V8CBqd_avPmwu$tI;*ueCOve>(B^0DJJQs;P zhhHQO3y6x65S77%k9bdSU-C9PqZI9Rg}xGtco{z8J7$zqU0vN#7oIs*Mk=!Kw|NZf zoX8_3CXKscH!m57)q(;vh0B62TtVr*9%6 z0av?5?72QhOWVhBh37_>Z=hkuAwEx`OKnpN>+<|KSJ#HPSJ#cfA@$eh!V{A$^luaH zm*JShMBt9QW=xNC514DF9kxoY+0IYy%vu`GG>@(ICzmfLGSM_#xZ}i2osZl~i`<)N zB4z1Xqh;wzk53g~2e^XUcwH41H)A-i5GcoV9d}ac{rjm&QWy^9B@=%flroO}&hYK< z)F|8DUo0zMkJk*W>iPVc2F-umvuFGD?*dkBl!}x2S|Qg;cb>(j3w{1{V>vd0!~L!j zxNMp|SQW~ijY@8-l^4J_3Hdo62k?-xLAE+*cUl3HTJ5UYo6UGlN1&2-S_`CbT=9fa zx)`3f5kes0Fk4wO3SVjkayj%^xts$2b zKm|CznzvlHVyj;Zq6S$aOdvCc7$k=PF1aV?adR{%8A0Hj(*hn99bMUwVN2<^7Yg%5 z?+;gR{-K6F$qN>R*C();Cvkaz7c5~mgM)%h+#oIB5c8@y=&MU@_*p8I5uYhEHklEL zU^ZK-Bv@}heE5(Ms*s21<}=-z-d)bK2wQUpMM{I{?Ax5>?b^bUi~z$WD9Q&7Qc9xx z1bsXBB8H|Kys(~r3+f81GVv4vZ1ZNhQhzXkJ^Hy@pONv7w&{QYBo_|29Jh*zi!)sh zCH&#Oe(+9YJnq@Q?8kJ&kQYnoXRYL~wv?9@()$*GdbrQ6wB#W~EkdJ`Is8MR43$t$ zS(*Kfwp>F0Y6l>v?Cx;~`fAAyT zlc9Jncbm3_Wt(fr-F_K(bhjBKDz}WSQy=YX^PPQn>iL2hz|&+&MgqFjKU%*_P;~lN z{GHbGcT*D3kaljY$0eVpAbe)spMkyF`svE4<7XVinD}^y9Tu4-0tS_0T%S?o4{bsM zd;*gnHGeXFAN$UXRI>;0HM(09k_P|z-YPBc$|D~ef^mz>Uf(Q4}u3R zl8rO^Wb?NNxbr0Moi2z*>AbrL<&71ZM=0atJhzNqJz7L3t#L=l!_! zI{wjQ-Jk|O4agfTC@A>7YNf@52VMdF;Rd1{FoyMRLDx71B#jEq<$K5Qnr{Soj)dC)39!n}E^o%5P0zhVUhZ|Jmnk@Nc`X0}vX(3u7t-U=nFh6g90 zIyhWiWJbxzu$G*9^ikCvWkSjx`wF@zAO;PbYs^@vAB){crl!m|DKJ~0oQ3$xewLm& zsBa!~JU~9Kc+lkTIY6#1fy5xyCk5!JG~FL#Cnn^8-H-rK1^|K$;Apr%?DoVhCQ^h? zmht?_k7T7kKX9}=Yewgn`@3uB1Dt>e2ttMo5FWs{S}e(9yBt&0etmlFdJJgj=ra3{ zAb#4W0qUcONb!HU0K5P&l9QABuAuN-D;E{@3y})aJU2UEfN4n`N}?iU;%v}VP{q8D znMDIblayUu4ZvW(q9SmL0}Z?}rcgjVxwtz@nYSbd4*9rAKl}9FgCqnYX{!b`${?@4 zXK6%Giq1NdwJP>~3thn5&Wapdx(ivp>` zz;YJ!VZeh7o4<&tJYz7MB6m--5VSxLc(kuMFbW1Ha8H+mj3r7HUf3FrnZlzwlST5+ zoGjC_rnGS0yYgFsGKs`AWuCruA&D|_28PN-6mPY9EA3y)rTw3!sg}q3se{mQ z*wHXBe1;4VwE4HlI0>Z<4462~x=ziUq$zS3knrI49e*6e8B>gNIv1Y&e z#aSCQ#z=bNt7uApga0${(+hOi2!bIX;Q{o4E-j|B`7+=rh{E+7tTnwrvIK6%YZw@q zev8>k-zUJ-1d*Yiwbuj1Geu_a$Uyw?#?82HebFWlL^~K!*8Hq25VLr zlh2M7AmV~XA<6sqUI_^}{QUfS7oGHaOn8C73iti{wb6U8JZTLLNn>N<6^S|%i#$}q zA#k90#rNE*$f`IyKNl4jM-}L}jj&!Et$Kf9epG{{7i1s4nA zg21jyz~vYL9L0<&!IkpcIa4dTcLDFnTi{;@GTG|u+1`_q0&b>{pPx?E>}R}CkdrXI z-?eK8CL4go!8#3yA1+r1n|sBjTw6Wv{L62{`rHv|r2EF&ap{^*pk`-hpT$7#Jk*M% z8=&KpFto+Ndr+#;l7xiM5)t(`8i0W4cz8j5di?V8AZ`@YJ(0z#0g~-H6{ERg*x=$} z;ou^Ti4Nr^C!&DOvZa zVY)CC!A|#7_%3<2+36hZIyXOmU~=&*7^e}!sE1w}Wjz*kfc1}?uuQ>0R5cqiYu0U1 zHx3-e!PR$lbzxv)0yHULdOube{jeQunYn1-#xl)<=Lm9|M}PxHg19i^o3}+pzZ9zpJjp~?wXnrQ>&!xx;^Q8xPmXO;(sl4<8sD?T&4ZR*2rbMv%{Z4OJeu{iDV5AJ zm-V44&?M={;C9*e=4Q?Pr&)V*QsvI)4Xtc^7q}Xdk(2us6j0(ykeIZNQ^NmfB5Ybj zzR8cmz{09Dou)lIJA+!L?L8Okugg6He|k9n#6t&IkVXl=zaqHpAN7nTd=Zp|aj)L^ zicmMd7yv5*XNt|;k#)Ig#OGGO;&7T68IM9(mt zE{zhDWlA11A|oO31cMQ1$nLL?^P8Jfz&KTzro+yv8A_lRh~omZx-lwf80hTjy3xHLZd=|u2C0P9bn0gDStfQrSSOF2~knZkoB%~WD>CT7lF6j>G?h+-WL%O6> zknZk~j{o7_`+nR?G6(*>4GpdL$)-i?_a_01AS*+ z_tRV$mL674mwnZDVD|^@CN{gt@>A){aM7Zf8>^u!y)P2~z=(vR1yNPyVGYfAJj4Eu z6j_5BD)W7;2`!%;9vPVm&?JG`a2+I9ISjTn4Hpq`vC(mH!xxUIgoKDgyM;A1(>P*Y zmWvqu-$oB;Tmg11rchJDKbRCA=rJ2DC7-8&$K@b3(KE1L~JQ5+l^ia|uAkR#~xdu)svI7NVq zAGpvRY|yw3M!+U=!M7N@`m6`;wcE7#y3hHrB((Mc5_x)hx-k=Wm%Y)*vW82P^AjD^ za;wFO3Ln@_{DQvz&CH+zbEV=A`%W-8l553e$A72^3Hmt3A&+Q{Q5?0kqFy#%k*YiE!EfS?++Bw zi+#&1ejJgo4Q7B~fh?`r8Av!u`6vQAKPoGu162h$dcdB04OA(GhX2V-zGaSI2X&lu z3U^XaIamH`TgFY}tdES25#Y+1i;>e+8agjIN>b~Z7ayyF`0#2e5;HZfc~^@hDR_`F z0hg_~I8+abhyvyJZ@>fyZ*P8()=ZM*PXTNb#InJmp*fK1+}_?+TbDuNaM{g%$CtdU z%E;@jcQADOI2IG9Xzy)2YJ=?-l$VzUf<6!tyrbVfznMI`Y%4V{8Mb1znqk-;P9w)| zFHPz+$OSeffZS@xl~E)tR)PsmQAz*S z@sbU8;=2r90)h{$xxpo-VQ0CuCn#I8m2yHC3#TghrW__Q$fwZlx`XNFW_3 zN#1F|O(247DAT^{9jHc8B74b@CI?2+Ic4#L!Q!cTJncqt=YQG~^${cnhrH?i)e!-Y z{TJxB$W=b~t{@!>9Rst!&Tej=LW%jr7p7B*lviZPkfTzG%*<@t;bD?<59v4m0i$il)JR*TLbdij zD+NgDz&PQ}O_NK{rn_JWZA5-tVxrjf@zPwY7k37?U9tuF%PWGU`To%RBq0I2;(Z}+ z!28lVXJcanMRsXfrAnP-0UMZ?kSYoivs9ivov3<6L5(x=G-=kLc{%sbNDcjxCz~1} zh(yEUDZ1T~>@dtX&YyfTmyBvg73Jq}9gUSmyczlhE z`v^|4m()z+g$^fH15WN2rXZk=!F14;WClz?4l{i+y6t!zb;K_uv==#j_=KM~+)plurDEQ7?_I53-T7tO z65sYB|0)YnC@~nPr-|qijeU-+kwD&FIB7G!0s6(F`Z$m=v6_z(ScD-nFfqjl`rH}R zX%(>s@x*jS0uNd+@)@Lx?Egu1sgS1p<+J!WQEPz=-uwO^yafy?U#XCj&svY|I`&DHAIOQ*dz_&Zu&J@XG7t z4$ZAk6PQHAOqH=+=-71NGI!tmz3|q8f15f;zt^l@%2H(U|mV&TYBkK6vR1z}`-c<^G#`l_sKUnXe zH#9s>SpX*An=BC$cZWOueur*Mgm0y#Bs4Tq6(*#XAZG!c*F@3^ky=AgnP+;R4;(JF zqySUnKX;uk6dly8lzbYHGXM~TrqaF&$YStR7WVOeTKv>>;2{8W=dzKPBtn|kQ5acM zT~3x;FXf~KI2e02y7!cnI2l<83q7my>D<3&P6h%UAWcfHcw`!@aR`UV+dPn_0?-Qt z$#@f$Fjq403PBMERH~8+3ZrPZOk($S_G=>Gc?6-#Z#(mW4|{xyQ3+mzUdAnNi{hL5NHyVSy4{YvvxR;Q!2$27O zZw!{p;!C2GO_ma^b#iUVA`2pvSqAN>XYpUk|`Ln_LWA>UuSMh)bYV|WwAG!Y`8cm+)~fWv^0 z#TN&xk=YzKrC!h?1%;R&Y-HdWIg0?q7Oh0VE_YZ&b&bPoWXWN`K@d(DgEZzPbA>_Q;e+1EnmUh>i~TE-P-Cdw?p# z+OA>v;^C=rLAF{zdo*K~CBv&Ednc{QNzf_V$=Blph&$buQCqPtgeiP#K~W&+fnsc{ zf+YeD1%+BjNdLSUt_!D+Lmr&v0QN8kqPXclf5Md{aDmI9^0_JwE2R9ddQj!|)sfZX(F{%u<({$|6yP6!^#Tj2)V2wB+HSq2msFBwKgik&@@U z{W_uCIx^R}&tfC|9vX*=+Pd=cE|@r|xR_u$44;O<{`s8u2&FW^+Fieb1rC1qvm$I( z`GkQ4>Kfzw7;@xmfOeVenA8|{!zL;-=u{c}y+6w2bqZ0b<>?aV_!7;mQ}x9di)po9 z+jcX?%XcBOJ)k=RTj8PyF}FMAY+Hkg;c z<@_Ti&VAD0>3+44-Q;BUV*Q%yUo*-ddDD2K7mfvr(O;MA2oR2yw*5iFlZKpfZApOh z>|szLra&fUA(5{6eQ0W)yA3Fg0J%9UAbt??_1F<`U(Bvp4}R_29!u5(WM{1^z5mW-W=*D&dz>y?;GdsYb*Yb zSqg7X;Luj_w{H%=djpG$`(o~0S|FCz1Zopx?Cc3%Crz;_dY)e;pD*FQepMn239lX2 z)-%H!VScc(EW7Q(ZtL*a$(FRXKCHT4__7i!AQ#Mjm$p}Z-NXn^L65Dil$$F*-I25Y z;kEgL_MvYjS;A&!Sjnc^c9T(;M-{Wasi~>hTkE(y9{ENC229!^{f*U49vGe zdY*fx+3x4k$M+*yAR4{OLyzeva6)WYzGDa0sDl8+1fgSl^n;)DV36as&pqbD_qNEL z4Q)ue%YqUb6G%xUeUcA>RoZ~1ph!w=WF$E=GfK%6ZToxxN$r30buthTZ5~Gw?{|J{ zw-pv{Rvm;1NY0)czg|E_hl4#`7_s~4@=ALkuI6hDCXuMdjI+$MBZ32XFhvv-wFm5p zD=76MoJr_J#L?e4->$f)|Dd<)w?|$yobG((m4c0tWsR1dEIkzytLRSx3t#?zUTrt< zK1?pUz;=bo^|>wV(rz(g4d-EJrhYeI`<7+ztqeV)O)X(yn0IT}&V{jNcLH_E>{l$^ zj0(FXF@~Y_A2p+!fC^F6+#HfVkC!;vxWoidm$0ZXgOT|<@0|}TYXvAPl*|kLguQ7u zk8W#%#|_i1K0Y+-MpriI4ihKUC$dmk6Xmy@`J|=7s7XoNhTJZ8Mk!~?)`JF4Bk|eunqMDzVf$^Ci!rq2>^D^O zw_ceAT=+3|1+$caFmAZ~%cEP5R1!64LK;Y9Ksd9Wua$Pxg$sDq;h(x8ZjP0f-3Bhth!#kxBh$Wd*GOHeY~~a+gsSTd1=w^(&AQg%}ByuQHkk5i9X?`n8V{x@rO93m(eB%Unc+Iow!w{Fhw%~Z&lR4|DC z^yJMxD|CHJx%A3G%9ve;(Dkk55iE2ABO|7JlSO12Z~^1g z015|^5R>!G0cA}oRG8YC!k!+LI3i)7)yi5tmcdIeJAE5)11${xVE=*s1H;SDWaPZt zm+7p^!(*Z;dpAfX^jAt~!S8%rYRhJBfk#$7ih>WA_a9qmd^SX(kp7kCM#InV7E4^U z)n(@rbHEQ7=6WMR&zBfBy_@FBg_eg*b!TH9oQ~d+&0~wbd4S{)W5S^dUdtYOxzW2S z8e0Y$*kFVh+hcsL->!vZi>&(-)mur|#nz+#$yWzFNKomqFx>A`FvFd|F3n{aS1 zUZA)CI!1)h?EKmwpRh&HJ>x37lI+Q{7kaDL20yzX)baCDZ;!FH5ES&FbHUo9_O&9jfX2K^LMn=p)0&#FgiPxgW z;MFVN1ipUoVfX;Sd40Ie^6aM5>CRBvlV{5mP?#Uh^rBwpNr!zo+qgwWWw7V|Zhu zxGJMS{k-FcS9>9#;rpdI9giV2VY;?f?d&7IM9U%;DjdvTbuAed91=FVh@QyI}bzc{Lh2;Dn5Y9Gz4d1yZ?mZgtPlfSR*FP}cR+ffUwR zp6>FRJ&V(}9LS0{k3rbZeF^Q{c0{1@V!hIqRb9>2B;@4$T|p{|sjuf^5+Y(1T#bZy zbad)5ZfhM~$b$41`TNH8AlG>FbAE6=rVK75rQy>7t!b`J@OXl@>3Ud%o$W%L`COd!B2-FDl&4ivo{ZibW8GPcjI?_kMU62~!Hf6o-YSWfzA$Fv8Hzp$D=3^d9=gTR!o4 zA#h=<51A&vtKeHjxtThxD4hr-{25h1 zQXtTUl6C$c7r@B;);RBOe<($0v=>`{dW(urO)MH@M@_dVoCH-t5q{9(ZYKv`Fq0{d!ilLSbthA1xA@!*~h8 zX_G>}M6bOSwKzrcEfm*qhe4Kn(?%tvkdjbbOl4%WQCj)kX2iljr0SF<(Z(1D);EVZ zWkJ5@l|HOQZ;zsiZt%)U|C+rKHa!Z1v=P1!&sFC(AwrW&Bd^COkit&JLMYf`^W%See2FOhxf+ z9D1Y>mios+#TwyaSJ(QV1q$!o*LtzPm6vaoeXr=@=-1=@1Nw?!q5)<5D^WSw_+Qcq zRN<^J!Z>PRuOMJC7=T>L`)bZSKFco^cy4V2|A_aZaNRK$TAL-qd+x+6x<6JhdWcUk zYDK?dYTdqrC6o&rF*h1q^cOnNzzTOVlZ8fXa(~4Ui4{^_1tomRBBnLYkh}^(V+qJJ z#x^?e+=Ys1lm!F~#pwDP+aa+5- zwQnYX6e*6>b4HCH+urQ8j3bGI1CvCBYE7*L5vq5LbdbQ;GKPU#Ug0Cc zo^f)b;S-N_o;#-F`G8{xkvuVIibc*_P$;=uSVsgZ8VRXUfQUdeHLT{L<4JfLs`xIKr@Exx;c0L`T5I7#R8YzBm0k5)*ncUaZPcGp^p%Z z_>gMLC!Ssv^Il>yAP)Ioyb0xwLc|azUYpIUWK*cj@!sSH9*Jij0V5ury65iS0WDKoG_Zhw2WOg1bfj4=DnR%QMnxWyksoH2gAFnI63d}(^xF}sty}2nUTcIf(`$smp%S7 zK|J%tNKmoo$xJ+9?mqaJTUVTy7aml&DO^53F+KjJ+t5Ys=9_4@qN)=o;S|dUie+of zA3yU9e~U$~VG&4>x5kak59Y*ByB^Q?ilPYdn~jzLODe#CRPlTuF-oTbxh4fYy(}nU zy24sNjQ~Y6Ks5y)FI5ubl>-Ci4GlqENeU;K@$T%6j>qKEl3-Iw-)4lr1lpR48HUpY z%HO*l(OlBoNn%u1m(jQrZliqyBj%>BjIjc6S_$5BBVuy+caMF2{l;2Y_t!^n%B7`c zsrk6f>}~LHrF%OAr&VX26)EPQ+glYU&O^ERGs8-0_7cg(YvUl1T-9vsP22n5o3`hz zdT~Us0cisq2_&SX!vDEi>JJ|>0Am1nZbh%0U6SW8f@Jvcya9%)0%nl2KV+|pJ3m}0j2oe6a!j{|<<>9pBIE7+I;3Ulh$f1P| zC{Q494ujS`(cK$ioTQ-8&>H7GWkBxh#R(LXmJY`eMuibZ4T)pQ7axP_S(Vg2Tu|@h zoYfm*B=5-+T)Wx0UA}JnKsA(wg@q+!M@$0pUAYV{r2mDKY0#75Id0sb(6WvLMZuZSE9y^4b#teQx4jQL_l9_7GsE+RG%n{!0;ij6G#9JxBHx8OVNzlf*2U8mP6H0+=qgG5F7I0V~Dur7+Za zdDj6f`8?L3egDZ(;#yROPvvqd=Nx!aIu3UQd;fCf#MZYLG&Q}yJ^iO{pHA8dRo;PY zL_FW$+TNBWPMtkG+omSv>DgQx!*U1+p~dnUA^jWR_zhq=280j;;MVX%yCDW-46I=! za3YcVg`7#4n4oEffe6z>vN7lH$`ck5L&e49JATdz@J(g4dg9QHQ(EezY9*P3_)IZX z)oG;~VPoT6+|C0+qMU&VD!EIAuwDBzqP2U_U_S+#l=BiHfkFM8J9=)P6TXV)ZA>1< z@y=HeOqquw&a$@na7|tv2Tf9;$Gx$e4Ps(kRzh}GR)=Zx0no~c|FJEpAqZ%5yiVI; zFWa&a=p)!o2j7FtL0nfi>p_pe@HRd^zPp0_Gq1$#dBAvrfl{*Epf^eA+9cp5;{eXsBcYFm^qJGwn|AAFY?G|COpfPfA>H$lY_@(}`W`O@9IYp7z^m=0*!#*m`I3whVW? zoPYitF6s^o?4yN- zt&^*qB6%rdjtLPfc@`Z0{NIz)o8&l%==k`u01NW}_@#WI9_ip;mBj&7I+XkklpvSw z91lZRum{Cj9`_1yjQ+#J4nJ_f1b}VO=jU5&KhKBCVeKnZ9fh}g4c{5p^LgjHmfHjA z5tzGb8DwOL5?Tsb3IT`h#Sl-LpBh{_5}*rj#C(VWbgP%6VFRmuWd+Z0VtK$CgA%Sa zNE{lk8*RjqL?$S?rez_A6L3NUF)^Uoqt=O{pTv9gleoc{= zmEH1Mxn}}=tWQKc`@7F z#(5b64%FAQNPOXD{{na}rJ=u-p{~EL;&p zQaIz(E4yVcbSAAQ>x_n9b=ddO)*?q`s*?Wsy7bq-iVbt$T9|y9!885NM2_@y9j#pZ zdWFvyD%pa?V$uY-5iJ^~f#!HU4O6!hqe1O~ZVml^*8o6@TW^W&zhfzn1M$anZtu_Ik|kPv_)QL!_Oj$A$2DJk>It z?Kq3cWr#pP*MHnsU-Z6+y&19d?QTF|HknC;dMeqZjUavyuzT8S*i?|RjIJMdMfDmp zd%B+?X20)IX?mz00^}|sOYXA&FKG4|W1Rs)D_|a4nSku*0nDs{b0avEJ*3w;j7h!M(GQP3Ec#`b|u>F!)Pe#UIV{Wh)x zRY%z$TqYoOvNZN>gXb1HDJ=1r>!Y*_wP_OQu^To|r4Usn6U2 zLB|wEI3b;3#F+U*9u1q!-ifZPD}2029!aw^$%OL_rl}}|`cwEzm(i~unyV|9H$N7L zyy*&v{&MK#^WM1Hv>`R!xFP=1+RBq_xu3EZ zJ`UzUE&5#RW_s>hJnW76ZO;nbF_kc;M-COLyi;A$Qlijk&d#W8irDM-`#P)V6>U0{ zjMV%(SxAkI>7{nhcksgic$qQq&jkQ}6FCCdQ`eoHYpabLF~(biNvi7Vv0h#v!;mUL zjS(yhu_697*7wF7IqM{U@b+{oWzPLvd6XwTzZcwj9L+?$NsV!$hg9Ww>v^h#TFy7K zA+PV6euuo>SINE{1~{wN_k0w4b_k8*N8b8N6?TY-Q*8F>uulf4OD2=MH4Yk7#jles zl;?iw$Tdb9I~6pV-Et-SkrRhv?myNO5?oc6;NqrrHw)F68+Pp$*gKe*jiz?K(SM{X z$-WaWdtf=LMY!3CaQjHB=W#E9{8_CI8BLhh*;wE>MD*cd8~=79)=z{4HIDvUPSiDP z1K1VcR7zx$P(eZ@45rgK9{%h{M?+&hwjt#$2+F%oJ?VnGJXXz0c5gP1@Ohsgb-@F z?x5+dqt!oGW5j~Al=pt$9^n&a5~ixE8iBxsQEAgwa?a^6``G!7%BKtE0pj07QX7%# zk6-ZpE6&G`K0nl#V@1i&kRPI5a&y`$tJ%o!k2Uz|~!< z9}?Ng`4PlOq2^n&r?Uqk_su=*+XLXbH#N(=xnG&>KQz9(eTqd-h0D$v+n??0pYyP< z08C9f!u5HSj?+bQ089*O2zjEimz#bCYE+P3-T(&q>HSu8B?%&>gb5wsK{mcB9yT#^ z$5j_I z#U8VfwWvzn(QmhgdF}@vaZd#Q`SCwqd0ufhNe>V8d|pYo7_V=A#B{kii|5#aQOGzk z=#Fr^wKjj_U!`i6<;%PBFiAnCg#o2{K6f$XalS(Q^Lrl&pA*&))2~GKlHS@@&7%<2JaA^|>%=KtTUl~$L>2<^}$CZUvq|j?+bPBXPA+eSi#>b z%PYN4qIEZ?@%%?7qx^lH&D?6|doiLn-Bc_)!}LvehqNPCpAUWhaX;)2#PTX0^hd~< z_f?Cu`K;-z4->`M5!3+Q3XvxmD#@sH@{;{?s_V<^`3g#ana|C8_>UBmHjX;Ufe%tr z($SI@RY_E(t8=k*rIv8#YZY3fS%lq_zrvXA5$8J%naeXWGx>kqf1sO#0~56E&RU)y z^^FP&ZZAjF-8YhUH2Z`!Jl~^zn4(8Cl zv~K}d1Z1F+M`zdO9w31O2JdI?{4WKCXkh<_A?P#xdc0b=@osZCyPwPkXr|f^jfo9D zkdFPs94hnhIT6 z>TCQ;t3_ZG(9jtLDIUo%qwtDsNYvRzGxZ*hktr4;SxcW>=sVdAf> zH)FRpX{nfnj1KzB~g9pRYVuY^i@$5+1f z4@XSLPbHlW*m@(qd+AH`+1mpxCw=;t?4x5Y0mopmJn5LD(Y>=k>G(FKRp~r zWPy81VeKw5q3K_R*NxBUkxmN>Y(Z8izE=0xHnr_e3UgTc>bHqp*}2DC&$1fkt^3){_0okddOy#om6T2U!QNo+ zYa^7;C(lWe$wD0}3C9};Dk3xC*|kw!Cz;~jjoafT&#M#tCp({bQO=jd*Km$TMP=0x z94{Q3K;7P)fM5|`Ix%7yEYVPyXFbaeyBeoWrDU6stIkf^_eXl%zRFP+b44q@BlEJ# zmi&VVS>I{>s(p!#{iQ0a2~7bb=o7fDxKPjf?X>8e>aP^6cL);Q&LQzkQva;14`0t& z?5i1T|BYhz>+AQ5W@S17L@1y%Spj?(5xu6clX%cR{GNM!uX@VG%41I`X}ED6Yg88} z{I!X`LtLz#d&nRBN`tH6amVah+TqOP$X?arp5%+Ya{60H3qTQ3-2WZHpO1pv{gXWW zEqp@v+`kz~YyM$YPi2HuK}zgMsP&6q`PO!7!c=$I>b|m_1#j_xjCu!t``d`!2Pkc7T@S_>Oe! z(U{TP#}3UgkD7NdD~h7W6t+Ed&tcd`+&%9o-KggH7!OOwi5^+hA1&&ap^Armv) zuo08+rVG0HH1P3DnLM-s;o-+b1$?UGMB^+;K**X>Rz_zwnkjB-O4;IeLdMOF4Ser` z-J}8t5kQCjL$4Jt7Qm3bk(w>8jNmVTo(nP7ZgvIp?L0ql2Bu4ZhX+)L)s?YFq;3xP zRTRJNs}|pGUh|xh^lLSg478x1u1>5#f=UFaRxjPfAVV_1@dnjn6|h}095Z5q+j!l7 z8QEB8Plar=@txLqYH;WF-lw7S+HZ7AI-A)}NZ94~w0@qu zm{iNDICl#4waTTF<%g%eH3l;1APj*G=)>D(_#em{u(uSy>j%OxM}%nhlvI%HKgE#d}7LDw4$P&T1)XX|u^fD7h(s zgaKU(3nKJTwMM)7f=@TbprS?OT6U)@ED3egiW7g@q_AvQ z9Bg0K8!J-T;Pd)u&||;YV7q=uP!pKC8vd?7x9ni5&B8EG^vOY1bL=($lQuF!Ac+>| z2l^1`P)yH)#m;y9KM*D(H+a^YkOM@&rH8(SEFB(lqhLWQ=qA^H(UC{1w7I+^UQzCP z{^iW|lW`Pc75r8ND#TKLf5K3EaJlTjzz0W>BALQa^}Md5#9P+<$FR`sCn99n+6I3vC7?r0(f7rD_IZYWxSMU?j$&6x zIWP_bf=@i`9bUsOnEzCrF~FvM4lpB?R8-iRMjHwX#qe}_`2I$7+;lkdXcLYmH~ch{ zq1qeutNt$cwU(##0Qwp2fsCCqgJD)k^Lmdc0E0c0;+-CZxU$;t@wKY2CCz*nlksDp zjE?49p;qgM81n1`l3YG9t=8YsNE+d;Gw+K@l$*1Hv1)-=9wPy7Xy;6tp9b#6 zhTyjAGEejbS2V)u*)vQ;>yp)qG+}r~B#c9-^9uGmYOV*;-WQ9Ma?vaw zy`+%2=j$iP^8apV*Fu`i5DT*2fR;^@h}$oS$a=6p*J~Rtls|ZxQV3Jkf2&>CTV&n- zc`SwW!D0<-vlK53Oz3^#@ir$+heR&ewM2t&d0+RCRM3LU_Bw82^@#zHGaztC8-p^4 zz!ZRlec=10s!bJ|{U4jj!r5>)>-)R_7}JC%IWDQ&Yb)rnfvH!iupKE2Z=YHx7Gj0$ z`$UwJSj6(dl&B=<2Ibg&?X|B}*2udzXFT4Ri8GWAUGd@Wu~p3E-bCiU4p0q3e#9vD zOw{t>{d2WxK5d;Jh+lq^9Y1I@Ch(h>f9EF!p=;$0{#X(iG(f+N3OgRT!9s@OSCQT9 zua067UJJp+%Kw0ZGI4Y+0twT0#Ko?Bde?qWf*R4G2;co7l$lX>>&l#X^}6ChBfes; zZrzT*gqsdLHlvYvG?g>KY5Pnx=2jjgPC z_}4W2%biSxxhecaQm}K%F4_KM1-}zta+?+Vr~Nz0_lzClPaZ;>#bMO92Ue7e7uEuU zxWYPv6j*X__!K)c;!n5Sj)4cP*`7~^$EE(e!*iLBfwH{xV}Ah5OLV0pFhVr)(K~&7 z;W9ZB+6L(9z9oyj%_AkLMWhVdYV5kuXv!HwHwq2_@0P#{?0@|PcRW?Xyy--BS(4)?`KHYVN>k8hFwTsY7F#jJHK+kMG zN)so<-!OILBAY5fpr|F4-^DYhBI%Q$XHHtp&QF^mDrM@zjej8nNG336cUF~-cbYq> za6$1JWnlc(0;T129wZD|6e&ol`-<>`M%#k}V2`~tgePU0NU5na6nzoWyIDHe+wgKFG zGRr7$K+5i4TB$w3PX>6${@4)72CL02J+6nsb$P5DGE#e(&1Y<+ulvftF2cACVR3FwQ~`R3P(So~S+(_PTB z@bFm?x-;YM@8Bkd>wb~{fM99_2QnNb5gy zZ&V5z%0+plMLdX*x_7n2RICy$@u?^fT{6Bv+kDCMYVl_8Yixf1pIWQy8vvA86aMgX zaV2LG(7vMpn|uC6fJ?9TBe$%~Qtp9BxIa_LxJhQ#T~iOWxqHo7>GNmMzqZ+VkBbx1 zHx4@wIWu*!QdX<&>wrAU;q3W3l(w=&&uo@4cH>1W9NWp>tEs82I(qfEi!O1=7}x8W z3c$bb(lGcD)#TCyhCB%Q4aY>kJo=&9O)|$O9pmlHKBG|@VN|~O?Cj1Emz+8(OfGqh zDM*E#0Zyt}X<3vhFN-`N&HQ9Qm||mP#OUTQ!OK&T#jmRcw42o8Ka0s`PKDx5TO?Bu z%v=Iw>KwnjcWIiMj{b-r>GuC9*V_^ikxgKA(R8pNZjWS0YNRH<#sD8*4phb4V>x4> z&pefysZ^on=Y%V5i`nIlRLOnYePzP9`O1)Qg|kTIrH<$!Mup88Tn0 z%aBm<*hM`MdXDqoJ;^o^xW&)?&f;Mb~6vvRNwd z1X^RjkD-VZz$$%kzyJZ@gf(s_%St{(|HI$$R8jlu!XE;pK`yl)AUPGav0<#Mt1Gx7_%F*3AnA9Y{cV09lU2^E;weHKtQ^t!R|6e=J@(FFc6u6> zh$zNX6M9j3%rZo6BwyT)Ya(R~MRHv2uZ&yTHHETsb_2nsf#X1tq=KR*aJ=2GF}bcC zl@js|?d}Bj9YDq8DEab2jlopD7cdw&`pIZ&&XwK>2r|Q`KQ!u&dA@25K8!&%aaqfBy#otQ4eJzq^FfZzA zRBj|*vKw(66vgCJq$nC!Wp!&z35~@WlxGa?*r(;CNgSjk4qOC5@+J_0^`BnojFQYx zyMC%Nl*88@Y>epW@ulVEuRFG!jl?L8g2h3vq%&K<>u;?G0tg#CSuw%_G4st@ph7lW z?#326ZCnbZNx#fB<7jy{v*UJf_jIf3G(#hUGz^QXeMui95W0qp?L1A|5Z7&!)8j;_vvvsr_5@VB)gab$t3#%k=in@Py;$s2^F`;t$W zW~?nX!jjlFqG<-Dos0Szq=%y(49#f`V>S~O6^UV3k405gai71dMW+M*_157R0jdJH zuuK-9N(TEvLRanS|B|(jhQHszLIILotr}Q^JP{yZney86WCfyIx{)HR)fRM9RNH! z0EM9kY)H5b7|`ejz8D%!j$65{oajM611i6Tmkq*$5r^WUK7=yi_M~uu!djjy1PYtOhRlO384VkwKfaSMT8ZaEUpoL7f=YZ!XTGxNZE+}RCKs3&^WnNsGr;mh5}c~ zSt(lQP2iO8u=O;td463Gv07>lD_C$zdbbUT7nf9XW^NZ)q@rYueQ$xm5^!P={!dvo z3dB=jN@OfRsKZ!c^0sbyQFF_x14c5q*gWQ)fzJS|oC+d2Twup`^g&vKfzpW&@X-hD zeY-u|!6B5m40TkzdDd4yb*-Y6x$5_F*X-x;y#-HiPiNm$2`K||T)0P`@p#ZvP$LX7 z0e?q!wK7!aD+hdVW2IRLE}iYqE?#h$WV_1b^~RNdwtt*e3aCY3K%&tpu?Y;kDa77~rDL@H1ZiP%F2i0UW?H^y%i)F=!)jR_Sme?EP zT87AQ3~Hi3lLBCEbWQZKAcFQ<$m-F^xS|Hl=f0pkx8nVSeb9Es0DLhxG8+*-&B(h%)MO9LY9Rl*4u}k@m_LLdH_=HVD9y9ENXN7 z6AJ1NWaL3!&17*M>qE3R2(NNhxT|PTd{UYDknVPQCq?i>zw=EpH`5!>`-qGlCpZkA z#{C=tKgH2pS>`Z01v5k*Ac6E)r+mpI40!w%(nup8BYu1TjDW&crJ2 z?y8+XesgSsf~LlhQL)@Hy)_m3r_E_+7?*8Do_?wGM@)AF(fljp)1ZJ=L%P6A9y@Mp zF^7?=oO{y_X?^IExzO^uouqgFxOEuL_7!9EtX-rJfJSUFL+b%QwqB zs0{oXkxczb6FHL+w!@nG-C4wxS!M)zmh|KJ>KKAfw4Bd3IppwI&7riaW{vkJI1DSQ zp<)O{@BP2zeC&v=tO)O$mpJSr=?r=0U-rsh)E8{jbW6Q<>%3vyh)|?47OJvE!o6IfE!fo59%no2z-E0!-{u>2HKT zgiY;{v9YCtVXR#$DCwKNy!Ix8NdBTyJhO4d9UJwc^h`x(w+$k?GUzN8~gQcK6t;yT91!{fGe zEPiQ-KQArGGRLU4ouY}LitpCnAGI`v1s! z%cv~7u4|a?IuTB~rKJP`3F%M)>F(~3lx~n#P>}AH?rxAqx>Gu(``vio&-=XJ`2JkO zYYf5T*vH;`tu@zNa}FbD=OqW#KX~ufPQkJkUr(a1R;pJ;AsnbkWnD!qdD`_%0g^(nZ4$K_X~b6rrt1m}tF2}{JIw%q-$i`SG)o5SD7 z&Psc%?~Pa`zxg5pw82q_a_ zy(@_N^y-Cu7jL7GZ0XxS^Z3v8(%$_j!uUC3QJ3sIkuNvx#;fLglFywNIEu_Y9mS+Zx*r)^E?scySDZ+h zy`|)BX*&aWkhi{;u4+`EjOT3g#tSFBl$mR@~#(f_UJiE0=uUN@?l9dJ#p#=e^lld7aLz5>TGOm;6R~I zI1*sW5jUBKcS>VLEiL*+(3rIH{-tx$DhcbPUAER!$#ygj+NjzV0u$LZa+F}Mw`2W8 zdi|+S71Dj+5(^T#C^CM@g>rzAlpA)VSv+55SZgoe=dWAm9zSc;IzBjk<@12$hrQJG zFxTuh((-j>iXVlmXI)_3qb_g5phw(bTXUks^|<+l;UR@(jF4`VqZY@zUwe?9zQLQP z{Z;hn*&MN^7ZWG6_qW}uMimb8Z@-eIz@q|+_4$;j+sDOdt8vVUmWqO*A9uggLLYE_ z)7t*NKlyM2zgjS&aJgbM!&LQQZv*+W`Z3YDtKfv3GCM~eX}HQQt8C7Y-N3U74icfB zS`&(Q&9Y^8ilo0*?cv;^ zeE5T{uj3}$>lq%kQx}yKs_R7)S@Z_E4a*egq`Gt^@fc0kMCz)gD)z5a!a<%WOQ_zCd+LLjWbB`r#H( z>oI;ztm*?Mg!#|%7vXVnfCR+U#wClMa&;e6t! z>$ePKVX0+FTZPVY^R11Z(=}0}LjRr6mKr<(06@&wuhf_{;5(j$fuadW4bI$5S!K%u z-L=dhdnbqCp4a@S=VM-1T8QD_k&m0N9`8RasQD00$1%WBNE&VqbqE|25U0&Yxa6*= zJAsq1X?Qn+d3AvV6S;JaEBoONbJAFUGbMT}oM&zJ7L)i*5&VKW{j%f?ZupATjxI=>szHlT9Lw1*W*s34Vn_k=g=TrTi=6ut~mD2HuaRV+T^opBbv8H&l0w_6rJ zK*O98ZA$SPu47b(oFmj)+n%!2`h@*$XFAaV=a0aw=^`@G|y=Vua?C}duC)VD|+ z1wl6R_r#VNal~;1w+G!J7d~35S|&K1?CkR@GDOzxaEsLw7JDu3i_p33`C`ImNI z+YmWL{p~N|N(P4&8n`5}FF5V8TB{8n_G~H9kH1_;&7Ed7;eIwqv1|@5uk{c5j!Xqy z5-#+bWIQ;&!en-FT~Pb*WwttJ=JvAs-eb?w`c^afnbC3SPXsC0DTK)T{w=bR_K4mH z?lJ!U`8TCk^s;tjVl&k~okmyXLUr-MyRj#%STx7eAIe)k5-cSr#2o**c7L^GamumuNWkZegf$Jl(<@MYmX?8dB_}QdV$RK z*ktFN|Yj!(kL68-&KT&6gM)__k(xZAg| z=PRNKxAE=GD_YCf-q)tv{Qx}_AX?iRilN3Yq}^c3IJBy3O$n}xeMCumNpkoT-}e=K ze+%2n>>}@W8T<_^&tQl)!fDO*=r;_8>gBe|YNX@hz}v=@P@z@T{O>IGj~*VCkKA7K zsti(?A%!d!%GpYO?tOpQ={-N+uuZ{=2Dn-w4kD+a+1y8uVi951pTqmO;=4LR9J(qFt~5_mPsp5lNT7s=M%EQV}M65UINy@y%bS#|@N@IIz7 zW}Yuv&g@*W9Q6MIU8K_=gA-ecPrb&S_yL5SePQ9B)(r0%C{)0j={3r6eRfZ2I9l;M zQQcn8Yj^6)DN8j*UckpmYV}=z-0WzsR^V%%#_D`q+y2$qf8myM!SMlhOEToBJjJe) zgN^9>$u#w09j~yf@RW6ktDcQm%tqF&yqXW~3#sMMy73n=&hsnR$Gy2C=o*+K9b2mp zize&@WZ1l}9k)BgdZTc<+m9aDOAfUiWe3a0aXgoeFDO-g;P2yN4F(?w&PG*prfBrv zn1J}g)3z7Wa=`Kls|4LnWPuDZMoTEr=w!r?Qz1VqDnRpI&&{=L(Sql*rm)@x%C0O= z$HzH%_8+_HrHm5yx9F6{&!e-(^cJ*qmzd|#-h`H^<|c95^$h+feWh_&G4SB)cYSTJ zrx0NX)g7~h3(9ctrOEt7tq6;U^xFL^&+cCqvdwTDjhEaC>86B@0`Bh(E!iJ>9X6wLOp~w)l5T zb16_4>&PrR>!Pv;dp`&ekEx1Jc9%w0+*Ubx6X)gbxwl_U#w@ei)Zy4zkL20gHNkve zj}Ee$uLzFt?5(vQtScM4Q0+M1{bv7aDcAu+Mhe^i>O#YfsB+u=<|FTelPYK9ny#CW z7fN+nO;u6NU$;dy`@yTip+71fFMXSB5Lh}mS^gqnwB`6*32qLigkQPwTUuG!pHx}& zk+4%*zc$tEVmGHE>82t^q*JbJ-(YjgK%lU5Fv<`?-&MfcNY;w|6$l1VT2R4OQd*>< zT+*?WR9H6J7DDOU`P@A;BwraUGQdZeTfo!4k&HYYrm88}|IzJD3O2{CqB_#-+=-{8 z`Fe@(+k4XCN83M9f|#gPkbicvqLVse~I@NWgxqc{$&|VdnVbrK7Oh~xm@IK8RK|p65uuurG&MxIM^_&3h%l9# z3;ZrH9br1)_KR)-p5jCR3?@aPrryN^M3)($oOW?wUiD?Xd&$)i<^DnvKA1wcF!g7eas5gd9V5jN%e46Q2DBr7|H1^wq@V-z;0Xd9SLv z!S>DR%zRhyZC!GJTf1G^{|>-H_`gj?y-|PIfZT<1A%{`V3J{X!r5w@G)a>W+d`N{QC5VAUbF1OB|F!v`7eCOC{eK2`+)tM7dMbCq+G$M%Y?CeaoQ0SkX#nugv{Q$wfKS7e|lj-fM zjM|NbMPF$fte5@pK?+QG`w)E&%~v`HDu!V3kT>eFjfWU^N$@0shM*x{w@kc*;wcV` z3o^3dHMVrIL>>bJUy_qC+F7X&qT$_WNchFx+i6G6!^*-AB5|$_scwTM`GeBJXfO*s z*neK(xW(FZJ(ZO3`cx%?^ffUMoQF^K@uu^>GC&+o{s?`zztEDFl|4AtcboEwkVpvK z>6~fKEHY2*f#!h_Wdgi3f>QWhKT_sTa}Z+mk;Q~m7kLK_nTlz;(^&;Ux_QkFK1heU zeah?f6ZGh(%n`TLL~?32VzKDI+>iRzO2Z3S5OWHZBX*r z+Mudi5ZF)5+3_|`O5$Li5bAjb0Xh@m)mA(!JtTSS&6iLbDk|9Xb8=o%I3P?2_i$0m z14d_*j)(alCFSJgkRQICUs;^65jyQ&Z};ZX>(KG@lK@3dcoA`N9UwQYzdf3fpTDA) zRS=KJ5+RvwV%%X}u0P4fte|y@j~v7xS@H2>x%XmX{uBps-@*G4A3Ys$32<2U9tbE6qmQ zv)^v5-0U57#!!i^o}f%j)&s}}RaWUsvbDG@CcE1)L(};MwagnM(#_!?kw87Z+Kw{0 z==fkWgLk$6!b}6mmY#!qASWjWNTcffbq7lFz^wb4Q5w9BWnT={OqInmZC15-GN179 z@NzKM44pPmi%PBbbj>MbQdAs|`JOs^fWG?d{JH3KhNXf_&X<N}CK3~X zNfu-YST5P1T*h|j&oH=&DL6*PS@o)I51CX?jEAEf0{bqAl(-<_&F17xGAC|4(d`AB zF_@1@1zLctpGpimgWL7leM3X@nJp57fm*K-5Qf*eNCruBdpe?m*>7jEh7w20P`*{y z@P<^pFUTyKKXA6@#U0CO^o~1%G;5!QWl}Dg+c+=-mkyve-T`FzL+T(ixB%y9+YKn*_@bf;o_^sCd~K%hdom@2R_69>+sx9upa=5lF4MMz zx0lHDOiq~L-Tpu{6$=3ZA;ST(@&2=e#Hyf7Ui>bstfR(vwq;L+Xt^kt|Fa9|x3hua zQ7YQ9PmZ)0NQjA0b=~t;L*a2e1~f|Abc>c!zX(l2Fq$b_4wwaD5ZQ5dr)~K=AR9@FK~0@$%aHgYTzDdVGW!;(P=~{osPY44{1r>r3}7>H*f?QL(JCWLhf5Ke+H4S~H_uC5iXb%DM8S9!AJe<;L z9>nLzUfcH#tdu!y<`8#u1Xia-U*MaEIbUlNy{|`qX}C(mvpOs!81_{vwT)zXQut!* zJ`?ma#?y?z`&hS2h>Ig97ZwuoIhn@`sDUq!oE#d;ph$hALY+X4tMh)m2Pogm&{?r3 zW$rlP=y8W+<{!HwW@C)h#Js%QJgGC_lH@It;9FJ3>gc}I4eF$3=+cm%xeEet=%?t? zuZzF=%ICc0Uo$4ufW`G3DS+o^yA(7_1B%Ix&6!`+ap;XCi;eJ@Q-%`lMw zBU>K;Puc0cGIi-UE?fPA9cYZ)LoCd74xas5mcOy!L?K3z8 z;KFKJ`I=X-OU+&>5N{Ks)BVu$YWB6nt=V|G z1pl5HA)kl70QvG+OdC1|OHTmvNnA8b4YF);N?jNV<5ynJm5qt`myqx|ZtIQCv)W161*boWMPcn=13Zl)pb+ z;w(kHsex@=G_>s(y#C9#`^$QttF2r!b9+#ur9#@%h2QBRfBE~D7|iuCgLy4R-isN9 zc0LLaGp47dv_r>v*Wd)?KZ=AbF8~iOay{BcFu1p=GW1H;s22xp%nPtFzx?=tsxxim zeaFJWV&hEa@@HFPpf;zl5RKo}`svt%n2;z zkHL|LVLaYrBc}UMV>rY9`O_(BX!x^LM9!SE={;pie`j7XBlwrk%~=V|7{d0Miw6f}G(5$0YFNvPOq!V5#kp0$_opQA)+Za50g@Vn`hBlc#Iq zWDy6Y9py0JmNuU5N50r^sdpQVLx9V>oqXvk_&Q zugnwur}ZA;d>&34kKGm?cBqE?tTg5GP_yU34B9yY=w~JmOCQt#!O-o7sMFzAm`hc5h!Ws5BU7dS=*->xxfN9@aVx>{Q&6l@TR68OlDi(6ba zD-#;d3EuI{s`K*Xgl@(f3Ha=iM+;uiX|vKN54!`U_QO$Rb%4fzEDPB`G}Ijdl!i~3 zID*8``iY@l_nYL8bH)sbn|7$%1AeM_df~zu=n?#@;`>WF!3})6zPJjv``5=u(lVSK z-+y39)34oLhB)5N&2)?3{eZR@qwkC)#vHr*!PZmDkHR5GPL>-4g!OwpSVx%JQg3)j zqh{>Ih~CVn1)FJhJvSel{cH_3joaQ3kHbDk$XY-1NXUFM_Ihu$xQk1x{**-L5YAvn z-@smo!+LOf*|9mPmlOHs@I9fP_cdaKr0xqTW~X7B7wiL1Z<08??V_TsJ?MAvCs~dF z>&?dKO59AF9wTh`rCN8J9$`?FL8nxDW~P6Dq`1O-XL>VOH#){ac#F%iZZhgm{Y~GsCnb?rD~L{ z!~SPy;@Nrhq9}fgjjlkt;VHEw1Fmr7m&-mpk`aXDm9Pv9`y}_1KGL#TpoNiK23O22K1S`trT#IqOUa5HP;PDxR7!q zU=Q5Msf^jtMGKE*ArM@pVV8F1=eZwh#m0T7^_Kk`4;g{g+M587*&++=b-+`$tORBj z#)t}^j4a`pZ_D7GrUwPW(H05^;@*`-_4IwAIleXvKRFxs>8YO94Wv(XgY6~f$C#Wv zmwp!((7D?6orUCNw}@>WI09kB%JE|=fRmqd&K2H|a}jjgj3*>Rzj^T0so zI$Tm5GXJ=4=*G<|(!Uy#>3#nZr55>cE=8I+0k&>h*j5h_1y*Nz^`l#voi1%mOvpq0 z0;1JnS$-;W%snL}p$FEd7yGm0uuHn$S7FKEw&PV*Qs;>;ypH`EZbyrc`d7yWvgnx1 zXxVu3cZ;GRZE!ABZhW6B(F+dVg2#~{p^&#gN&Uq~JUs2l>}+qht(4a@E%!V!KR(g{ zZ*kl~CNBsQe7LO(JrIoso$#%vs2d&k<;#;t3!wKovNIU^5krqtmTwzin8Hds_4IA) z;PPKwyc1WL<3Bp;6LJ(FEo$|%=UaVGpC2Nk1X~~`d^L{fl(6=EEhM+&Aiw83(KO{_ zYP`WOBGMB_RK|N)H6Jf@pJQtIw5`B(vX&#~&$ovyBr$Chjw79oM|UKh!B~he%Bm_E zdWv6#P^j-KMG(@jh+}U$>u@;sbF+gbbouJ6(VJ^>2AG9`^wdK{d*8Q8c~L$x9#MZQQUz1paV@ zTo;fKdK@-9i@{YIX@oJYBti;6qLN_8O-J7uc=OCx8E#%=%S7|)jddgUyuW)CxWr9c zpSFt`J7LVXZ{2?B400|cxxolNs&MUSs1OWwdv_Fw1pP^$62!+7!sM-vt>lTuRZ0L& z!vD56uqyiot-sGPJ%SJW<3#;TBO7eVy={Jjuc8imqim4Xwdek$i-j?T3Dqn%?gK)( zunqZ6(r@5J^E@3L(ggfQ0F~8nxnSb5!W|dF~IWWiL-V_Erm+{tN(x zfq1j1t}Z#UUuL*g`YN__#VQ(%;yWLX+bA>2X9~-4FHc+!V^7$~HEb**I6dAK2D1uY>X(Ca-plLWLbh+G_YSJfyEe zUJ5yhfuiw$p@@SeZ=e$P^&d zlIGkL{?cahBic|(mS{+_?e&eWxqk8ymmPQeI! z;R3tN^`#B93HP^(xWW~(5OtWyg6L!ncTU;435KMx&PVtP*3W-AU-15vg;7L>E-K$4 z29Y#`v=Ma4<1tVvt$vlgKc0S;+J3*h5O#}M$uY_e^_lmE52nTJF`;>c2}EG#YtEJE zMCP>8I)j2)*aO}8?tV~M*8+#2JzzWz3{C@P=u4>M%GxB#JftbrGZ^HvS9n1&Mi+SO z<9RJK9@q-`YL1Iy{`Y5GOGNmu`aCfdKje4<>gAV6C)_LFlyrUHa=~>2{`P1Ri(Ngc;p}As#yyBapk`jb~Af%z8!R4o(qM|?QGdg=pkH#J! z4ve9~7=1x>>fP5AW}?ChllnNKXhhbt#qWd}{L)}w8SiR|@db4K_!OH_zy!6`iO7rc zm>?+gZPh8n!PF5q>>72E+9?^RiFx2^gg3b;&h=qoWOxDWIsS`v*w~~Y@&YutmgMzE zzy+QCo$>z!bWS&jB2YK7ZP%lv-rWRwtEHt?jQ)o+0z9nNT>@V=N0}3!ino;Vw(6oc zN0%z~yk2cP3CTROm$L+QdM7MuN=m@w2)8j^A}MVsg-ajkbJ{iE+dWwk0z3zR9ayiU1a65;W`+5m-PBa8GLT3n()=HH{$iC}ZnMzshq& z_{aj+j*Kd-0GZo)ZT&UbN0W5rXg?z`j}M5KVFd(ZM9%JU!{|^DdGGzKJz4cf1PJGo zim;r(DnhoIv9Jo>{;W!H&cOFIp`+nW#8bAeomjfnC(mH=DG_iP5ptQ!EAIhYI$#d! za13^Z8s`rg@H_Gg3O>6p0vO|kR!u|z07@5^`V_KGuGc=CoPJoQ@mbM-PDgu#hK6hi zk-wWI@Y_e@;$-P0$dJEN;(qSg%@-7xU+}XhcbQul&2N=PvYQ)_V$~5}IqCGKy(4e` z&Z1=xd?i={#?3$5q!=dA-*na(0kJ-=D&&D1Hm zC)?rIj;6|cWlxp&IX+mw12j!QUFR?du(Y6mE|LA;f;^+4LcQ{j-6Q#>rQx$R_!52q z>!%V6G3d%4<6ybP!O*PIna^A&e^5dN9)`#L%%zkZ9FSu-FWB%ESp;~(uu+GZbSm|; z3x3|)uhcS)budQ{TV#h8I^qg=NZ=dgj{s1=8w`OMY4S#Z|K-=+iX;_8qLGT;;V9LJ zTuYnIpikt#l)VE>?6#8=ACnM+Ltb`_WUIS8-R5w&B1#_4 zNqEwu_YlU|+KN!S;OSWOt&v|0KvMwR(*dR+Dk`U7lIXz&+<(_Zm=LJ@(AE#~t&c#~nUCUV_8qJmkPebG32mms^J<_*oTIRVYB&Li0&1 zxXmGZ)`0B)`9VViQ%MX?f+q=YZ6XQV+|9Y|K=PfZ0I(qhN-5XF5{#g%v%-@Q0Yg&A z;6vVIgYIR%<%V;AxW;n;wB{=j%~gIat&!Nsz>Mx)zom0>4_&HSF|E7|S)xbCjsrw# z4iaDgj?6bFk|14M`9+^*?N|@|vw*kU1BlTIX~r*q#3UvXJM4`EwTY)w1P}O`6K_RJ z7BP@~To%j4rKS#xfSB~=r(Ub~zZ*%FR1O(9 z$OIJLe}kC8OBODa6R_R=h3jedqW+so6q-5h9X+DPw!{7CGD+Zza?7z9C3vAe_~}$u zTK4n<#Nww`XzlDp-fz>+Q^$84whJc?ZqmhOV(Nzw*ac<6G!D4-TF>9F-K^dhEp;}`PZ^F+wys76*!2!Kr0_mwq1!Jk z_pFZVy%Ah_d{E88D-T;I)(>Kr3lICQQXED92aDLXJRSxF^#7YjU z59F06Tn?~IHErE76QBem^?y}qm^$_Q1d>0XZ(3=ZrPk4XB6 zw5x*1{=-v(JK*3$5~^;3tUY~~(ZYGWSo%Tcj|uO@Trf%>^mLyfJh2-Sa~&wbVh?;0 zAXlDN&MGX#{ibp&uTx2(`kwScRT&1?zCNW)^F>TYIB5m`t*L>d@jwqYDFxW*BcIr-0?Z|`*1szSg>$G3F0o<11pv8vZIzvjDv%E zts^oWR2QapI=r;y4p%bk#1}*Hzc-YoCR*d-(%N5uHY%Lw(1SQQ#3kFAE)pRB=SX|% z_em{4F=zgAePz`VJh;m!W%(fPL|MORyU)0cDcoVrEvE8qrMf;7&cT7ZZh@5{3@&6G z78p{41E)Mq7&AIpty^sT+;rty=^@M_4XQ6G`}+5R&Mi^l4GngN-e}#dMR5fSuwL@M zdf*2T3J0&@Ukl!wyMl!C?ydatM87j-Nhj1F!*Cys9f^~iQ8@| zV#jqU$m*z-K%%<3z4b3T6>w8?nD`>KHoz8dFt|Oo5YPw{0Kk250d8~VB_Yy(M++qnnxzIno&uV$MAOLkzO={vo5jtE9{QR9O5Tm!~J=jQmbvz>;VBJbVt6|H|6TsBv) z*-W@u@|}O0XD}Sasf$Uuq6U`Hl2N37Ufy{-;~vq;=PQ_Mjps9 z6wrJ7Dk>_V6u^h39SpcPx}LtuuPgsZ4`By20ETNeJUWN)U%3w_O672w@m zLoj*ch()i+-@d(X*l>PxHr7d)iP>H@XwZIZ`)Q9`7}l0|r$Nr^F!vQpqBtFOsm<8< zt7*mR^fDAMJ1^Ms&gK0cxcZ;6#jSr2!3he)&ZwV1om0#Y|M%8s-swP>Q1BlY-QC^& zlK7Q;%B68h0Xv{Q-XJ5N2}HR;I%dVMN(~#YN@nYkP6vd#PJ16QVOIV&*mo|FoGhh& zVi5e}tS&pnilrIY#TutSn)xjO>HI2|<6rj$EPU{+wIoO*z^?~932{6})o^^QkY|!v zu(t6e&>~Erp%D4KJUz1mJwOB~Ecv&aA685aiK5^&=>%G{kT@UmY2R5=wJcoH7Kb4^ z$|VIU^(N2ll#2YZ{moOwwZ#qYr2sLooM8ZKhEiDl{Lkt6J^oUkTVv&9yKHvtOKReh zF;PG}@W_0S$ER6ae-`uQ`a97B2++C^P~4nS1q5<6*3*>M$%O znp_1o8E`lTfGYx=+Wdi6mZUQ^$P>g{jQ)UG`$Vte{{otn;0reb^XrL;no7E2D04B? zD!#RaF(mnOg#y0;=PcF&AqPzRGmfWiNK}dY6l(cnVA0d7cc@^8Gs>)4j(m6i;@EYtewp6H6!CK~}qrCR+e=#&p-B zkJ}S9EVW9@^Mu+`kh`sT1GvltUQJ$og5Cu%?r{aKH)7k;@fya%8@X%E3wYm@Ji!!kQGY1rEcA;-UyZp18So|~;!N%O{!{DZX0McrXCBLq2QdIYhJ0DA z?#uV=m8?(E>#MzHtAmVY2BYbIeIo-0XXz4pCY7`QzN7-Mg6sk=F|PqlQ(PxsK_Ng@ zl-y>b#(Y=^NWs|Fl2n>D`z<`+9$UkSV^Ugr@o2zHKVG^cVyy8jIFB(r8inQG5?!}OD6t*2ThG&43PW;C17Y&}r{{OXAYb1N+_t>qmQ7XUDGJVe=X z5T!Z~nV$**kZU<-nwDs5F;)(|2fuB&o5d#wz;CDVL`DJM3pznw z3FC*}){L5^z}OxNw1* z+rw^1RP4PoyW!Z|=l|i%UF{C!@a|kG3r}QP`e2qcuRkMYQ-q`N zx%wci3j5nHBjDrlPNsz0nL_(TGRK7W0Qb{@CxL$iuk!whue20A@JB$u)CVwZKv~>9 zCPjfi={|!T0zI z%`p>tdyi#O+Lcq04l{BO)YB0I$zSNF8>PW>170dNvZo*=Sp zzGX0fc-WRr=7mrU>cL1bJdZi=pUJUE#=b_RTvW4SjSpk6UbJuXf6%*L1!95!+-4s3Jbvu^HcKzJPC+WdBq_6NvpA(WNLblWJE(pV|Uq4n6XxdRhIR$ zRD@;mMkIgiXH4MnAxh7I4i4`{!12Xo-189E%9Bn*zz!0Q*QBfoeKGns!A zfIq}$6lzQ}COA4l6#Jn!D_X$cVv7P%V)ND?=Qq5$1!3GH%~YvYG)HHw7ZLRLpVoRW z9-}t`4XD$LcWlMW>LJ&s1k^%8tx8Lvv|LJB)6mw&fDsr2`*D{NbPslSEn!;zomDT9 z_psu*v~wDq>03xWPRRC^;wJ4drLouGXVN)TER7Ox$Pg72oluoHKYiQ?BU# zNy7CPZtM6nQdUOkc#Qv~KiZ0at1EO7LWx#V-QXs=(qST4e;toA5E5scZfh zq>~4A!}P3bC1$h_Cp>@AfW6F`LjFxLS`Z>4;Y(w7U{`Gh?3$2(3Vrtshmrlm^_B!_ zv3vyDL<}RPjRYz6F8S}VGC;WL`AwU~gNZws8W7gpD*ENzw-`a0B>e(eoOVSQSr{>! zz}Z<&+k4god4GtBjqT_}r2>8ru#Aptqrn5fgLp2lqJlrhk?@bR3tH%~a;4AAnm%z$ z&P0htZ6Vy53~=(g_Gzf!bSpMp-0Xo4S8ph!Q$gOI0|}FqaEzN~sMws-9HQbYnkkmU z_U^r2XY1A-#1}>eIiunu#gbkb>6YAU4Ah!(Uo*^fw-uL0svKjMU_$n!p{L3?6AMtk z{sY>zEMLPH?iM*HPtwi+UL{s}TEL%RJBJg4ad~xlU+?=Hi6)lGbm7SfRU>3YI2N5l zDfW}+E(agPY!>Pu{4Un;*=>UwX~2r{J-hQHqSi4{EA4Bxzm`uXR07XyaOV(Vg92OLbxL7`n1qt_ zM)2X%C$qB_`UbJ>j+So(nC69ur`U{PJ$i4E0qJyjB{okSpyW&ieW4!saL)+~gGfNI z0w{Cc1<#OI+jpKBRQ>?R6!3oP=&O}LjSSTIesO?GXO}Ye7lMikRiLnf0tA3~Jt9=p zndH9?T-FyampXSRtyY})IIL&nNu7>Gc+W<~%2&sik8RyKp!Kma={@Uqy-mvnr8zm) z=wil2w`d=GUe0HhmEjP4ND5w9SfGnGF*)zOyQWxc4Z81?ShJANQ2pf^gw&`@zOb=mmkYL(Il%!dVSt?}fmeO8j_O~B<=u*(i# z?qXCL?uuhHBOfJWWMTQ)tM-ePmG#N|HxCXZW*AR&wD@>rE6Hl7f5}3%07OCU)Xf

fBA{jIb_NS99@y3d*>}mbLxF1T+?0hU=W?3)Jo+_pYo^%jYtj4_sGH6p$6&F5q>)SLHj!1`AdmbcdcvN0aI=({?BmYYZ$ozhPN zwkSlm=@PE!>RhSRhUV3(i04cgn&2MoMtb@YyjXN6s?~G1GP4m7npKgGTpJg+ms^EE ztqh~*g)?P=;d8Ii6|3o8@Yvg@*?{|Ew1b z0@;(Ra~c}>dK@?mwg8qK{Qks#wpB!{TO9 zT$u8Bz&eqL-}UhCVkr^u_F*@j*mgl4e};=||LAo|O+}TF6!~7PCP>J9%(apQFy?K} zPnK{e7(k^aL7>z10TG;5aBRJ7mczor2&(|T9i131exH1Mg1Ixe?(5wcTahfug9 z0OoJ_p5{;0{h4x1cjP^7r&?bLBgkac_Fk+MXpwk$ezTTP(B~q)E&$y{Jb1cT)SaCj z8}fkJZ&_vlmYDeSoHEwph7~lPQYG{Q>Gj^gmsHz`VoiL@~gGf_K42(UkVNOBXa6 ze{SAx9lo>+^mW!LpFVKRm^m-ven(Vo(QAad1H3KRP=6?uxZPoG2g^aAUvJeF{&h z^a@DuPav!zHP`UcBKbt|csl+HS(4P?ufKZrO1;*W4C-@1M3?f0C@<{|D)xepPvsab_{TEg zKR+GWT?Sc|GGOrQS#{jG;w6^{|Kq+AfCE}Dx-5VXUru0PAYQY_gV==BJg|xdi61>{ z-3lBA*-Jbj&qtluuwj7S^bHMl1LQWs0}v8M1WsSj$q5iSZVtD%Ba=C`A^rCh>jGB` zxi{Cw-b_!62=ofX|NoiT!~~bN>v=WGj}t!@ty=x{P1#YdpOfi9IMmjK(&E#niiWPU ztjclmo$C%#&E8fKbk9I2`!Xg}6g0iqVjT|khnBNdXaKM9%3(edHEr+LwsosF-0>fz zrQ3kOawtu-04}(%-{^Xrt3;5v3S92GByt%2JFwy$#$3b-%F1~E4skVL>!RMy8gl{S zPqW8mW=&1aufK`825r8u?Udr#*>V3L(rjl=Yi1RWu{kM?JhA(icSN{+G4_p%49k_s zR@22DpLvaR18{_}<5E%wiGv|IIB(RuFF=O|bW3bbi1{^58eN1HeE0WB!Ra>FH0KV4W6Tk9S!IJC z<28j9M*TEcn-9krR5}pS9_n`uU+!=!xz9TPdGKMjy#Bk*bq1(Rs;mLO$(~QA&hh5V zlmc8!Knf{n(lTJu(&F#c)w2&>Dr!1GRweaFN3BB99(Md-(n170MHm$*%fTNq<*0HZ zKB>hlHulFP#bc{*3Ud+y3h4s|I_+_`JYJ3gZV2v!yC(#W_IW*Zj0F{yxY(40swx(D zvDUokDD@2^b;`q*tIqTZP(3glPM($BtiHS1V-ongO`~36%m^Br3c9*cG|{|pmrEXO zQT&H9WhQp52i(9!1ISMQ!cCrd`?DJjE!_P;?y(IX`^J8! zyuYbtV4k1ctN_L(?5Tva=j8(jf!&sVg-r4)lb0bj-D!du3@!et?Xwh-aOjG`Ll~%C zT%PUduqudwLS+yN3helKqRJC<*HP-Ep+b4)a5`y`GJVSN>=?FB8ZzU}9PlBq%L9roVMj_?nZ}q9DNI z!F2fK1!7E9$Navm8Q?;G*=-kililZ@15@?&g#x93=rq^E`nt=d+cF@*puhU1L|#{SX}!o8l;R53 zKZZ8BquclU+fQk!GY>`oANnf$QL`(^W@30Kv9IBgsac0Ni+*GT4q>1Cm%nP_Mk$Jl z!2LWhVsQdmUCH0@aCSPvyIcve&b-KqR88>2g^L*qc6_MEk7l zmA3c;+BROp5gj5WV)iI3Bjuf*SM*LxdhjtDP0mrhrq3%#t8Mjx1m9GyF@Rd0h5%-~ z(mem7vNjxds7~{PN3$PoM+Sbw#~n#P+Nq1+ed`lr762*sI8R9put@=`Lr+z5z7&e7 zrZlR~I}}tDDu=#|5JRBqQg>U@XbnZz*2!K8@M4krOAaMV~;ygCfzOsA!`7^J<0`G$Rz!2|a zlj8}X`LETRrHRd!=ok!qjya;D4J;8u+p+T@ew!B6t#hC3^;!CZ@*v zBdNjlf#5Qq(bI|4vTJ;0U0;qI*_~Kz)p0oR>(>(@tG>5--3#0WnKS=j4|g=8|1dDw zjww6+362R?Nd+zOY@4s?B8Xq1u{B-c_PZQdRwU#3*|ZCpJjWsY?2s!F@_6!7;CotH zh-?yf$RdyBnYriv&~$rRY;3H<))_xQm4t#>KqoM8_kPmga@N|xo_DvtSK?o!dWu;s z?@@>b{bO)Pa{|=}aAJ;k`JAXIv+ZuAR_I`55}k;n_;#g16PCa>n!YYVZQ5%O4OZtS zHv%E=bEx!|V^wo9;8jntSc$T-*hx*RU&Di0HI6s<^u;31|}TJ z(gWc7&MD`K>CWHgmT$eh$gFGXSiKy_fB$Z9p4QQh4~6*slNB9V%o0&=K;+(l5^8+fcJC3v0~wzy@jl1bDnxP> zoQB>#nST>QIiD6p4BfhqQO}IHP<3oeyiS!P^Yti32vOgSe4BGLSgSjQ#Bn`O{$>N$ zR4pu-uiSYDvRlPsTwTn2@Ahl@RidY9Dg9ys0~pJn%Vq>!g>7X$*9?V23kQ&5}w92e$;F{ zn7V2bV>i=4&*)Qzs@n`l&*ALSquwYs7mMVv$t+wTIWK}J($ONd>1t7>y8?>t2fWKU zeEwxmg(5j5?%XEPucL~>k}IxWb-bd?*VuptGZi7V55R`-2T? zCf(4EL*uzHFpQgLG*KxyWYCe;$9>|O@RAHT1yAMBNq!m4pkHvZK17eW^yO^f#|t4D z-I`Daknh`te@Jfx`ORBTw~UdP)~13!1xO=$-8(saWg;Y`gn)qPe0DJYu4ADvf(6Eq zy86R#i$OUTHnHB3UpaZ@U=&D|7zj~tKw|Nn2GmyKY`oTNb^$hd%aq#;{m@Q~%!R0- z*SgR}7GMYMD~oD(Ku*VgG#feyk2{x5mu0 zrWkiR(E7E`$^~VPS0D$=V)@;r7`r~i{-W$6k;gT^?OlILtA9AwBbqrQ#Bs52GU?Mt zk3htXL@sqVw*TUdpF7*lVv&|6lE>qa572xz`U14B{ncX>2$XP->(rFf(D&E~Cej6? zh*Mex;P#JtJIQWSFTecxIIwm?EPbTX)~?jQq4~e!wy1iCFkhhC1O`Mly;KL*m*Y>{ zWuq6S)PGU2cgt+`?07e5CXB(4(S0`HIvW1U1l1>|jN!GsoF=R6E~!xoJ0tn|ON(9CS8{?V;2h7q^HWWTX?pugcJ(1=vG-wR)y1x!qt{QFbo<+K z_umC(4_avK`;sqS$D)YDqExFzLXmVn5er!x<(B%}QFGlg!55#FsISqRm5w=rq0}6d zRV~nrDzX{jS*~dU`uh0Sf!NqslBF?4dh2AN(TWw3MFRS8=cMsf*Ok5|rJRnx*(eT> z3>xZorz?Y6kWk`4_=)Wsw8-f{L5CMt&wS^^1J^sD-M2ue456HOE2Ql18FRo!Xs)kl^7*&eY$wRLvA9e zBN8NY(N4lU(aXu*Okx~NYb{yCL*TK;`p?&PraqgyX|;X0?blHjbLn5%o!Sy|HOp*z zno_;kkMMWO0rvB?v8@m(HciemC+v2adc|O=CtOs0cM>x?)i~52-uk^La0~lN_L5Ab z43AFC05AQJH#0G6GjX&%Z{XSI+TVBSt2cD7pUxS178@9@3*#XPyyPW;T;#O(T4f=O zv7vpm7$?)#jhcwYi*L97h9c@XU8OXX{xHs={dQD1QucZ+~}{vLv168ekQ2b97+_d_MLBr zmQLfoX`h@jx&>WGAi_;e;%uJd;QwxnQKR4q_neBR9S)fa52ukh2{$V`o48~V5+Wpj z_fETJ4WG_uQ3Ixw0&PY>IficF!qK0kAqua*kGXATkL->rz9ueHYg}mc8^c!rMYcMR zaoVn;XD5uf_t>}ZU&Ae9c)f*n(>dMk!C`Euw(=3#>y(dnb2^ti=09|CMe0a!aWc1Z z<@&~plm8=1JB75Pg*l9_MGb+v6`Y{Vyzo2NkdVyfx5GH{$zprI_rO2+if4Hd(+w61 z#rBAe3=$=EO~pa-CkE%H%vBXb2DLVsqW*c>MJ9S!=r?YtzNSF3u`$=*|5oePx=zWhe%vOz$Gx3a6u2;vquerhCGX;6BTAlRZMIGA6_?0W^`#%YR_ z)~<#Q^4)M;R;PG~LyW7fC%DYOLW9YYBVmGf6ONlPXGDwga{Bl$so?M~TF}$U#naH; z?CMM#H9R^m7vbAhyE;+!lay7ZdFi?7pI3G{xw+5X)?td_eeT z?oA;N+MqPa&q%~G{}??jjd&NNTx+jF6dM|=3e$2Y*V~uP%R~42EcSCdL>Q*7#X14h zvcrYK9%X@93}UuK{%3f!AT}LeJN$gNU*)0WJq!P4!yxS-ibQvcjvq;pg$@n$tt<-a zrU~gIcT~A#LZuuUf^JpzZtkVtrA!agT|(Eq%)qOO#~`!gEjjMWjA;gt4mLcXZp%Y? zl=dkilmH&tyS^uu)(#1`6iAao)Vs1h9hpJe(x5JRC*Hr&Y`O_Vqw_{Ew921&G1eP4jxF+jYuYz z^j{{yJvYC=V(!9skwsC#BF+?6tp~uHfn=ZiJA(`U26@e7cPURdQy&GbHucqxu>3IAkAB7 z#dD>ff%PS9i^aE3K@M*IXRO>yB=EWB{7@6LXBuHLG#feym6KZM}5HmEAzLXoV3!WI$onPQ6t|CfVnn`H! z5vp_oG+O*5&+_GeCKxH0@yVhyNqjVz->ts!Ow|*2+;OH<-utJ7rA_)zAnbxwNygaW zjQ6V8kJ@^XvJV`V?e5__z0B{EV}CqY-fak3-hMNP{7rM@6x#8{lFvn;79eF+(7|7_9)?fC*!1x_}+(P zY;Hv7=_X574FQD`ll}56&-QWf4W=jEeKci#%RcxD%aX5=Up^z0CT#Q&O|L)Ffo(&f z``>oT58gAT#k0G;4ebuS_kyQgNXh;vt7h?A{&;YhHy)6N0Y+0N44c9nN~UWr#HL)A z*oiHGskhlcjB|OI{_O8OF~tax8dIn3>Njm(!i2=8_s6rV%w#o-7B?6d%dqhH^T=#KSl zf5!b&z_Q~VmEgj6f4di}!$yu@i%8B-KN|xo8=k-Z%G61RDX%kVw|Sg|%Gr)MmV<(t zMHR!RjOh7ix859awKye#F^Yf^)1^DW;{#Car-N&XTYUYiKSqBElNU6RKAfL^8Eg>X zM_yH*OpnuQe4shdm_&W|NXX($Qof+<=u${5Nl z?LvRogdfG(6@iv}4EZsdj+wF}d7x*cDjr@0@4C0Y^1~)v{H{Y|Mx?&yU zzm4{D{Y#lZ$SNU41qMYV8Zfn^q6naH4j}~Qh`}##BjYn}LLmI7>^s5Perx?;lcYd# zVJ=qfmiLikm#4(*wba;C@mxpDUaF;d++a$Q0-?o5r4@Q*Wz%+lDj46 z_)c8+<4E2I3OmlOYWt~d)jKO8fa7097?+M27i*J}laI}!c}}s<%q}H7C%Jl+I9fRf zcff!0oOWw$Lapg&qu1R*j>~X{%Ve6nQXvkVQs=!;?H|o^^FAo+IrwyG8j_*B&wC?y zr6c7QI#y@K*22NP@aX{J!S3tSHB5wo^MOnWB1Y_Yo=}Ecs!wDrO&$Ii9RC`TF}n~W z(*dJOiamR1ybq@?W0R61x3(O$^B+qdEVH6yA_%wiuBxiG1^n6@eKP$oB{n6AW!8Uo zB+=eKvP~kq8*f0_7phzTeRTeG>ROJ8APvl#E;ElzgW~!1;YuI{G{T^5^fU-!QzdB@ zu|h~GrhxC?)s$F-qIib7hQ=J{AT`I&JM09w&3#HMX^C%t2U1LLgVv~QL_wt1D zr}*wYuA1EkQS=hny7?51=M{w1cN08;J|*%S-4+L-M`7IEGCxLh&%m;!JwZP-F_DTv z$ro%On_y;iE5sibJVa6tHUd&9Gt}#CW1Q9D!E$7_cL9Z3-7S;I7 zT0;p%&0470*Q~UH2qFU~eG+T5n}djD0&L8G%)b7G4Qq6t;VIm4i?uarA^nz5%}}fL zL__CgS9mz~fVH)C(TfotOTiERwyj!ofy#ve!kQDtHN7?tqH*Du4d(|E_mVwLT3@CR zL~^|)-)JB7y5ZDq=hymCOk{_{DzmIi(Dx>MIvLZ0?Aq@h%F5WN)TE{!1Bb}CsVRUk z3x!+xoF2en3vJm7X0oPdu5y&=L7!w897LHd=lm){cz>7+04eg8^1$r3u;mRDh#*ms+u2ay(5Hcdr0EA=f2b#I zi!QA*nt()0IgH$TF? zVQ|E{e5%5V?(?e!Ieb5By|QbR3VB2G)qgHt>Xa6APHff%!Iel3y_de#edr=_TvwCE zS#3N+2LqdpB5a-d+->bus4>70kqA+w%_YeR@vU6$+~x}e3RGB6LLze_(4mGwGuA5O z(7Z}Ydle)30Z#G*;@SuJ$q!;MJl-L1OmCzV47L7!ZoJo}F;!!qUa*fXxh)eQ1kJlB zT9H>O))IH`-fgXMSw(U8b91xti2$rPb~NKu3`$dih>>;geM=}Xi7NE<({k}N+mq#( zyJZ{E&S58xaT68W@lmO(#}a=|im@DGvYb=nCF#d_mHwZYTzkRqT3fGBGXc--A_58{ z=*=4%{j2tCq1;&6O2on>(+bGAfk0>tAxQcx1-X64zV=(rI*Qhu^G)VI)m1IOuR3-6 z{(ZuKj2T};1qkQpx5?2n46{OVt%1tWY;0LR5jJJA=>e}8os7WR*%x$ruvhEmU>{p! zB=ro>{@L4eCh~wB)b}bvQ%P1sljDv-RtUNl60)l%TVatz88NZyX39P(=7E;T7VNm} zTeKHpHF1#p^j_-hgWjx5h8_C?4#y}51Zo*HxBK`?2X(=@=<-aQoVW_11Xf%8R0JgQ z?uM>jlwD<(W(0C0M5~{8Tw6g+r8sRe3;kL2bIHP-4X((O_Z0F-O&CS-eJk%OTy3MH z+M>xsIT2g_rhQ=BgzP_mgfBSVJ6>GQZ7VFHoa{;C z%aPx5V0|+pnaA9-tofyn{r&4#X+z-k;M{j1{9=UCigi6{4n-~pwx;FY&_v5YR}ZdD zRnXC)e*WSGZ9QBgQ@e+;X-c&gy51MY8N3(E8&cwtYYyPsvsL0wS^3?*amH=~POh<^wCy_+g#V(UIsUk)YJOJg? z;dwO=_1CS^$nb)47h$&M5wgg?35F4H!7*RIPEk22*}J-Qv3np|-wxw*pBWl&G5n44dxg;aBKdr~F?b2=R-UGMI8}~(USlEKYA$NxY@g<}3E2@CR#6*fee)}>Gv-4$Q zs(APzcQsx=F$o-6@gG`gnwz&K)6#uScC{mO3&P>C_-U zT?Jg7T@s+f;GQNHbo<||P1k&)7(zwZZ*qw-T1QiR7X3XNc$_%*_M}ZM~Wv+ zOuR6EQjab0MOk>^92P;blcQ25k_j`q0v)wb72mJK86)Sk8y7% z`7+_D?i>|$brO`&K@xZZO-s*(JdL!p>Po%EvU(awaYIQK6S;GQCrpZ~g2-J0@E>gu zMKOp4szeim#~1B=P;N_^hNC1R6}ex(+CHy=7`_1SIA-<+x59QLuYBrTOtD*}9!A~) z6ZZ8(jd2*#lA+dh23dc{DH@W%L*?XfGiE67iHK+KU+0VtYbE*AhOS#WTQ-KbGr91! z$cq@JBlH~JcV>3rSXo8mM|>|K0BULnFcYlc!B z$3EV65|!bhfEN9FgTeh)Mjv9uqg|K-F7&q zwmRu^XaMG~;#|7U7D0$MP>h65I5g5{L#>mVjff9>xIMqa?Y^_$nMSM@%z#HR*-)q8 zC-$GByaMciOJwhWEyCpq9KyOPO3Rg#r2jXW>vu}pt(<*lSBQqVV2cT|s>FTaP)#j# z>5F#qiEYno4of!EYC^e=>bBy*AU{1y4G00M6K0S|4&>(LIn6eqv9)*kbK~cTL5QOy zHq;AX_F+3a-ih4mR(l^F5iw7?nECr;avUh_ks0u{NoSu|>(a5*efdW*H<)VJIj=fc zkYP2HCcGnObjEsk`(FKp{HJ@VM6j^>Axh!3%!K1zU9K|;<}EA@QEV*7_1j=0O0e2^ zGQRr)HuClRAA)yzAGH2RN^QwDxPoq5T)u8m%#$aLLtkwgikwHaXenIs4I?hMn zBjC4xX3IzAxUu+R3wrVk-}4GOq7P*v!A|0-5(rO^iP^sR99j>rr5xMchOiq1;a#yu zpB3(TPfX?_i^KlX8CRj zvdYjfFWm%{x+!>vp0UCn7MIhyiPZs?d$jNnXODFs(#7txu^m_s?A&n?5;QSEKDZrl zP2j2t=Qm`rO|iHY>0`z?NAnqhTV~4yjl$cFb38(;4!QQg8M-^&c^(qU#9RigXe&7}#1v#T)%IOuo$bBw^ zTTc$>qpYH0h?KnFIL{~Qh{9T@E69-nQrR)BX+wD=!hdb)&bC<3To1;MPAYgc(%oXx zg!t`)XQUYSoaEH=^!SzL;z zelhNW6BE!(T#s*z^>9BD@iQ@DE=8r?D`Ku|+L^1?cQYRd{%>iW!&)vjOM;9G-g;@# zgz~>$4kBr=p``e#9q?)T_Hy(m;X*@E&1JJalIzrtUr_KXCxUsrx}<%aczAw3GTXz~ zu-=>ylEoqzr}HC42;Z|lMa2n+lZVHg6#~`JqlXV$lp*8%cRue_f5fBQZh%&x6DYX$=5xuW$*3QReJ1F)SZ35S@mb5 zR%!qSzXX?*@SlMKBT?XwGJ_b}pl<2}{0#`}Lg79@PvgJ?Muc8+;phJxvUVcezV(T) z3Hw^|xvM|@uL%gGttRB-J$(aE8&M5%M%>#ykfO)!aO>I8KK!K4IA zbG^w1V>J8v{l|6iXV77UTU<~Y9f|DhyfU>?#s*HJ$0DY>va+(Q{ZDwop6Cui$8am! za=|(K)o}J)%9ra#s5BP=DHc|^r~{t9Qb2Fp6Z#vAcPPu4b(b?;3~{J+37s%&$o_z( zcQ8J09m%Elfexet7>JYW*^t+=N=gzOdw4)rE&2QbP;;(;^0#myvVcI-!lNWkQH;StX}}u8wHl; zsDNddDQd&Ikc{1E)mjSV(Z}&AYv?~M7viRICABK3P-3cya~3W&huqQ!vaQ%>)Z)pn zq9N8vy#mTIWXOPejDeC@kzUaycVmOasTmnT9GG|J9`GPFMf-|Y2gS@Lj~roU zKkSz9VZToKw7NRFde8lNBdFN+MLS>7?|$KM2ir@249R#Y$okdTo;GQ$$U5qi1YQlA zyYWIEsnO`A+Bz8_J6w_WrZ+*l5;V=We6U!rVc4Pfhnb0g+kP7ChC5p%D4E5sVSNb- zo&5;vQJpu}GcWx$_f};Phf{}@8&WOr5FejC$RQ$!H{6G5m9}d}FZJNRN!=v>{T?Hr zOQT!|3P*OoqP`}m^a8x{2rI9DK^zoTZl+?ap zPOksgT*n%+vx9D4_w^;exjm3f`^Ko~1HX242Z&0f1EzAeYf1EbgQ+K-_EUTo1 zMGM7&=UX_+sWqXn4p`q$HydM=+@_){6B;Ak;tSiL;wKW1#-@qJjAoO|>!00|x!-it zfqHu@&Tl@e`o;D(d#dxQ@auejee!}Ve(pNXim`LN=dNx5&`Hl!6&IjPZP+fHV+5fW z32aj9H{eJzxjW)j+;YUg)8I}cNP{bn>>`QPJd{ND_xC4hYJc*>pUYo@V)`ZM=8gN( zm<;>faz+c3Ml-tlVwm*(YYk0x~9;?c$*$uCY2=>%79G<$_K;>@R(YG&G9X^Dy zKBgr@qvOn1HZ;U~_4{3B$J5rdBc`*xx$dKbJYM&=m6@<<{&@#=>hHq25+ColpTy@p z>d2P;Nmc_3+$H_-d8!a^*wwHRdw&Ny1sq^>0o>52f5^l67=fms%*1~Kd**U0Xhtq_ z@$H4(1p!f5y}?9RUcbUPO@rv+jBk+UAKBCJqkkmnXb1XIV5Eyt0YlXOTfFLLsb# z_6KEQxTTnCqoMBFSE4w$J1pWB< z*j5rk%d|+8UDfdMf1G5bl{A>F%3{gMBQi{P7QZ3!@T^^ZlOLdF$SQS&}7tm+K3 z>>?)CB&J=i^{+TPiqfTUMb<<`&d;+DFiQUrmVpd8;ix|_%B+|0rGL&bX%YpN#NnH~GOcfQkKyL+9Q5;RKZ8_XqK zt;T%rzr`G95xy6H+1DiAM)xmV1oz+C7UgBe?Z*N_<+G;}c2(Im1b^;*2%fITFtRa$ ze6uxve@`|P%ym3G#6WZJt_-M}efHO{1dsFnA?EDuka$O9xlO+ORq|X>MP;FOvq1so zHbXiA2_YmW!6X0_7LD1$K5YW{GAw`W`r_j6j2tFn0HOX4+kLV+w)@tNP+CoY1+t## znlRC1q-~2Ot6jOE@?+{qC4#Mu46JjnQ;kNen^r|YV zN7)bB*;Bm(RTw-l!OB*q;bi6Jy$^!8pvnT|FGpBwVfQd$?&FK=nGQ43@M>vXh_|yv z?%8z1MVj&T>sPsWEX1d*EEZv53IqxT2EF0#E#EVDL{n^fYq4>&R8dkxOV*{2e`KLS zfWXhha4&}j+;2XoJIe-u7Ov7wWbH^D_>9i0B8A)2;0Ck2Q1Rg4t)ZBN>Sw&(&|b zEzAb^9Jx->)LpFHRJHA#Q0x#+xQfdh?`16l!%**e z7@(YKHIo)1Q_ijX9O8*G8a|?GG43t`;Wp(%3@$HBa%dm@5O>i0`J1%RMr-bKVwVH01PYKHTo0t68wMVUco2KMdDG)ZK^_9UO5@y-j!P~g%p9W#9dbD9B^epH zHF&A{9yqB59#>25hLWJu3fs5Q<8QVlIh=Lo`L_q0d!-Lq_p^&pGc$)Sh~eEQD%X`s zW2TQGL(Zibg?ow-p+ht0^R;WDMSi@YF;q)KTlu~?+%z_N740uuKcEBs1{2k*MEHYE zR5J-)RY|(gM*N8GI$DMY>|0s zb&p&mpu_Xt{{D} zazFXPzWwVi?*d#&2>)`M5l`|m5Ze2*q4}I%SI8a!ykF4$H>Lbw2hU{h<$PNr7UIja z*M`nDWV$?3HhaKq);~b>;|r>GrOwzv3BG6a&OOhxFrR-KfzzNokY(Vku|R`sUGMbE z?wWr+W{WOfXYmb&b~}_MFluC`6+QTl{DbN$Npe^Q{s*^fLZ<|!5Is-z;uYJ;GF+L9 zBUUa7{5^50j%ZQJ$juu1`YT@PYN^vzXC>W^`$Gan4wt@l2P-!X^Bb#*keuf@vYA72 zc?0nL!{*w9%9z3aY_~hT3X57(A4+ix|n4@=$WgbUY&h)Cda}0>6Rlv==w>iW3 z-%QvbzI#btP7Vzfti?a)?A>OEybE8q0EB+1#F#Shn!-rl8uxPTVhq@`B6MznJ8iG%W@Fb!kVD z#Kd8(R>ay~%ie^tM+-0XRew%3r|x_(&2o?>O+Jc?yGg)R&ruG`I%;m*(H_D=5SJ0$h%w~62$|t z5g1((Qc_Y(T0Ml=82?V^XRRksybAp6>Mv-bJ17$7{!1*`xJQ1&+6+%U#GeBF2Agco z=x!KAg1%`}Upo5g@E8e2mN3^(PDQ)=2e`S}MA?>9Qn4%OwCK68#PW|uG}=>b@f{Ly ziR?$q7ou=21XI4nVCxU=Z zw5b2F?a~BaT_e$uO82dWX4`uW%Qt?cf`fk;G5#`DA^Qg*iD;F=7?|gaVlDimlYFzG zLKRJm-^xhPBS>PcYXkLtl|+B{x|w_@!}^n2I6HGW4ER;c)|ka(DuNA*%d4d~CaEdQ z2PBomLuw0OB5kum3VfL|s$VPW&fh3vExSMW^dMnJIt0_@}H^ zeVE{h*Fz<2%OOYu&El(39a^Kc4!kHps9I!d&~%2u=&Ru~+if3gA)35`g3uvr=+IOB z+i`&EqJwA>)e>U(!ZXm4Y&-Y!lRukWe1I=!Znh-|WALWk1(5H^(V6Rqt_M>1$4dIh zG2Qmh9d=>=GNp_V;I6x4Vkth+;stUHDT2@i1H}luNSYpj3d*giL#0t2lHiF=?cmp} zD)P{)gs>*M`?Yi%zWb|pH)q=8u2GA6{rZIco_4V2#K#^ZU`7+(*^>c=kpc*x!d!3; zqSHdyPtyx-hMhtN;Lxl$hMOd8Rb*SukeZ*Tyvg>tfZrl0L4C-xdn6yf+(3B+ zoi*UA74sRsqTtb1eqHKhB`*W;pL=^5efKni4l`L=N%^f<8MJjm8)eQoUw5v_Ah#*nS{D~8=@j?$o@?ovbp&&WrX`XY0f?0BQ zl~A*X17P@rtlXAI4iJS&(MvMsOK#G+ALs}$cnHGe7j~-dc>4Bp;s+|j%|Ys#<^6Zb z(Iax@ACu*f#A=a4dpEeYDO*>9|90AP9iePlc||+epZjNUxPz1wbf<;d4W?N39|Uj^ z!0CB15q<+!7!LC`)TFHIp|qqlG!cX{-Z791B$pZ4|KFn7PUjMQ2>C!yb+HfbpV@9( zvvP0WzAc;BudM!WCb07^xWap11#}`SC5)Jp?@2N$q=QM&pBS1b+$)PPtk)!_#3**KPTof-lr9r&xqt{ znQuR2W;NsdVZOVa+z-C9n!#Qh$2WPBeW|n5Qhk46Pz=_6{76@&&-3rFSi^)W5s?H! zIka$f)y^=)+cU<17)Y!Gb$QFSeTw-scRyY+Qlp;_-wlQ4nt#bkKe=_}D+wf$WI~o0#DTHcW$hkOx*` zC0s!I!7vbr5O*rdXuiL_9dR71t{0u!171)`>Z|M8eSC$#W?556%zK2FdrFiJevqY16)N_vuvTyW;86G2U*A6sVd> zkGeQNT^QDLSb9Am2Z-ht(7Uj`>%)i{l_BG(CAxbTddG)^A4oX3xNIx;E&iL1E^qiT z3B9<X|kW{car7&?ZC6gb52?5e%XCKI&H)E#th} ziHFa8`^v+(H%=Ri->N`?)(0}D#==N>q?>XuyY}4AuQ_?yItfm5M9-0AbP}PBKkA5} zQa~6NCo*f27p{9^(C!Ls4-bxzl9Fvm6IxGB-TgFfSFYAKA8bl79mDpk9%ORP08~T6 zAO2gfxX4tA$$+UClC1zjWY_%d?(Vi3S#>}mVgdq52BDeHo%4_N@H7Y8uUnU<(LP9E zHImf-B*vly%5h>Y(K>gxzFL8=5^~x^9;F!*qTmX4H0K6J7@XF z(A&u*A*EjOSn+$YIpO{%W~fW`9CQ2RX`R*)qQD5gKo+;cQ^!vdv&^^G6RiR|oFY=l z1XAD*_2c$MO^5=khNb`v6oTpeYiV(@$-K0fnZtN;j4bX?==9a19C~ameoD?Bz4nK6VWB#H2k; z3iJC-wWm)^nnx#q~HG6B@8|2B*hgbqPh zXj;>i{f2-&vaflowFV=Pf~zTjows!Jaaz?UByauMjcGkQOid7<%!Nk**StA`zVVEh zvGBZ*U4UiOxakpZW3-gl;arc%%-)>f`6ri@RQ*(? zN#(?Apozk)2VqfwPX!^mgihIS#@bOwp9N;Byk=9q!em_=CM#}u?V)Z_VPAq#UdKtqcCXDBha#KiOd{mR|!a`Rx$w?kgMb}{xPO?U5b5TIKa={Efix;D%IN2XPX`lyZ zP3P*?*Ah<{OwhJ^Q)CvDS%Hu9LZ%E~X8)o@nF0$55uMqzvm^O~O=e@~z?n@V>p}AP zN=0B`pm9YaDK+Vm@ApDV#8y<4W|9UrvR8~KC(lhoBs)0r)FA5aiP8KkvcdZale_Q# zsi82xM%Vo;L+)*56HA@(v1De@)?ZxoDG>f%1*r+ZoW2*w7BH9y4dODq0U!xTwn%&l z3`8HUfc%r_&+}@>wb5I`m{@NOvpf4IKVHpZ*kpK^pp)Kj^*YsWF#FDhqT;h>&%z_s z{4;VLInm)Way{77#!YxH7*l+=BwnfxkjA)&`rPY-igH2R%D-Lzw^ zcaD?ABEsHz^)p<8G`BJGKRI0*vC6UD?vo8v{w1H;FB><>8~rH+dwl%x)A%8qrHw$3 z;66H27|}Ckm#x?XT-|VU)++(`Up&$xXY@P6Ma@|ShC2+JDr_sW8sGzL>8s9e%AbHI zNVL)~G!&({KvIJTCd_($C|=T=&(&RL1t0-YLUQudoB^3chB;ZXK69w?+W4^qnU1|^ z8>gf9@}ua}(2J%EpYX^?vrN+0BQOXVe9&;8tQTd$_Z8v3K-}C#Jz0@o#6cNo3&A9S5#|&WxDuB> zq%9r;=k1@#lJ8X<5zip$0Q*h)ih5W==mr9lEHPRQTpNFym(KO2D}+Z!ulKEIeg4cW zAn>Nq-~L|V05caC`sL{ct-j-&rfOW)!GkHEfr5T3W?abV?PVW6bO;U!Dee#NvH=9L zImIG1XyCIfR+jO{kG_gZI}6>fPi=J*dE_LtKnTE^OuCcajDT%i3DlC4(LVz_5a zVCoDC*5-<{t8p^1UgckD3p`MiLBR= z%#D{dkec`4Z+SwZ+t#g4~c}LH>4;PFIJ34u5|dV}BY)9Nze zxUH-0_sQVD1H1}g9SQ6bbse3TaDTg{Qsq+^6&MjG^$-Oy2dg+yK8697RaI3)m&0Iw z?immN5AYO=m~*B;v;@NvGM<#_k&o0Q2yfjgrV|Se_s$ww zz2$e|EqU5zrd@q-S2oT%h~fs_1Il6F9m1b*!6KG!<~!aLhZI zC``M(Jyolx`$jHOne`=|%(Kk=F?g=aD?ninYc~ym5)|5$Fn7!ZXrcpufufl%57xYWG=U9-{_2Ff50H5KOlSb8~NOkLMLOnchMf z>c9_}!-0Lt?|xbo_l*5nt$FEKwn;lIm$$~lhv<>*&wo{barAq&kZ<$% zaiYhzPSxHA9Z{e8YSrZ_$`Y*r>wvTO0wBxu$6rr-w3aE41JHl?O-v@2Z!^k4#sV^j^eJRc-RTkPWxKuBn46wWL~z?-V{#j+X$o+A{CkPxE30A z@IyN6KNSJH-6=H16$%aOMKNzcQ#g3aUd6;@?)FT~{}6)Fz_Bh6MGQm1rFZV$1(ztI z1TI!l4kR-+hN+oZ|IJ%qdz>77ROMb*YD+DB;%HZCfAxN=tl~|@NSO)_7`zQ3ULFa{ zn+{w~vyLda&FR|+f5UoDlfC88)`G>QrMcPWAYVUGgEgeS(;qYKlQy$N>Uvxw+d|x@ z9AhJkl#%-!Qlwt~FIh=U(;d^AsBh%7mnF2J1t zRFFikf%}BN-;22MfL36WGv``2|Kmc#piTy_!9u$`b3U=?h?WSNB#Jo`@kVL0|BFq~ zSIfXW8n_=&V_SQnPqA1Dey4Pzj%kk!{F3xgrB(eYaHm%Eb?K!P1tpqKJN=k39~bHb zN&B2Iv9sd<0h#apJ#EKM`tYcTgSrjvDw4-}L!}=F{75Es(C+x4cC8Z@l;7%TAlw=2tk~Wz6{R-xtG)7qG=0>Ou z=YtLTj)%_iEYX@U`RwUPN{h_JHrd6#hw$jFxy7sUvvB#_tsQ_(cOB{wYouxGQ+Pk$ zGLw`tmD->=;xACuMJ#M+vHLVSPybwYsAvc5FbECfB(th&^v$#WTDOOh3hx^Z##e!< zymJN&{w)Z#Cuehe3ly`_PLfCBnKcgGCwqTeJ0NsDrk8O2DmQf#y4iSNjqUjMXy_y8 z#Hgsmu{+nsL|LEq-JU7D9z=nDm>?H=J;g8!;>_Mdu2GxVk$5>Q+^GRwP=sv@IX0|!#Y{+=#9viknJmFm75=NE8( zh4Di0i2-meBLMPC6k0u@sC)sV0p;SCum@mx)%>aZS_Xj;w?S|tw+A})-PD%Lf(%m2_T}mDrr5i*-5d{PR>F)0C5ET%R5^0f=ZWKh66hS&fx~1#R^S$@|?&Wv% zkLOYMK5MVN=A2`WIYx8)n_-=o_k;um@6JerN)mkk-A(9O@lH0nSryUyaPjgcw8J)E z7j>k58!6-XJ7)0ef>d;_5> z%C^97PK^ucX_$j%LroViohmBUO^J3bit;k8~OgOcN+M| z9^10L@JxITQ?eb7^~io15E$fu-U3(^liIV*Ob((VO&Tnp6|0l+YpIAqH$$l-D&!o4 z&GC;l_eE$YuNbqEl8PW-uJKh<`aWyneJYr*_(Qc5ikort)B{C2lmwAkn(6;(QUn8~ zz&pOp3XQ`kmhr9yr55v0zySnhn0702K12A3r!UO zUwzLB;!g2B?%aLB>T(q^>O707BVhei``>nEJP?W^%zE+s9%Zf??5Hh*V)}sUgIw!R zL-G-=J&C!=Cj5RsgdZ6gOfHw1i`Cz^n!E;(8qCFjiI-)?4S+r9Z6fEoK+#Y=;qEYy z-~S&G%6}J?a>j$WqG8?E|KO5?fbkAqDHl^25}Q8Wn%bL;8#J3f+4DY-xdv?^4W|Rq zddEvl%ede5z4mvKzf?USf#ni;}2U!AOUbjaZ$3%LEgFKwf!*sTp6#*B=N zE=xZ*SJz6*AGFdgkiS?(L_{-DGy4taRYOll8v9K-quXp=AX zagehYR#`4sP!6HXD+&`>XGDSypdf(3!sS25#!L^_iL2Oyu+49uyq!i6ASLx6xZoxn zoL^eo+J=rVMcCk`r*^!4G8g)^q6q_HzUc$qpw&(JaT;8GAv6;Cf2$nPSaEYZy}&=| z1-)BB@=P(O$hjRW5HqyaJ9Yg*dp}12-E4H!6jxS^$Gc@#+ljNkdVUY7*1Z6~?=_gF zqZQgPn|*Wt%*nR6q$Q1;ggw<;5Q>MAK^m;#fkIZs+mr%H=#f!1JfW_NvPM&CIOFAj z-(mbCI+{ffMnv6>828HzJw&8INGo@Iq4eg0NOrV!WF9whT zX#oxL6rOGbF92~Epwc~md=4tv3kRq2G5ATn=r|pS5A&VXOx#>-YIr%=(@|{{w&$FK)2j)0)DJPLvUzsa-{fmzCZ#Byh? z94A(FrChm)@?F%r3-j5~z8jR!>7oHx)t2-TkkM_w`Ni; zRK+w$NvxclQCb{8xzss+jXw+!pY$aK z#eW>H_|nqoCJ{f^?8Oh(P(4YENdUxb_gzA3i%LC>k8q>6GT_`3PVgE5PSOYIZjFW$ zRc0!RlBvj@pP41zIM z3nMOeu84D5&S|s#{tl|D)6tlcu8Aut&IDDPu{;l2>~fj#TJvmn`Sr$X@nBF|ML_}m z3I-;;A+bQNTu|#N|4j)$Q8>Dgpw-2CqQvfY^A0npF#}X|(#@FN!%k)F|5?T`!Y^%e zyMo-&s1240>^wm_q3?+pOFn`E2SXeD0-&Q<87Q;9=Fz~VauNXY(6sh$f3w2vKDJ_s zU2fi!yj(2P+h1vy8x>BM?-0hk-ydp&OI;Wo9KSUsx-@KTjIA)fx8Lv6@NY!DTZ-RB zdp{4$T}-#2sFfFHyAcj&1mC}BL`dwkkO#Ii)?Y4CrAwP(d2aJ`fngF-P98bzDop6q z-intg>d=+?BeM!@Ate84^Bq6O6Cgkb#q3}UeF7|4>?$67IF%6uVV#gc*m54{VY>{U zt}LuaP|a`fOLrE#kPF;)qvQbdqWJIn=?D@0zIQ#(w~83&JD&z&cZ-v4myN%4oWrEGs{7Mp<&y-P?cRL&z<-WoUpqU~B} zwx!;|NoWmtWs*!AftJ*sxb*qFu^wWF`r@pIabXX8`}0fUOMX{J{ct;eY20NAQIoY4 zl@~7rnB8Z@=KO`G56H0DkKOIZZ%{>5)QPVZyxiUI59%%Z*b=H=V`=(5U0_1zF1b+si6$q*bhBme`wPvY%dRFZ*Fj3O3xcA7!S^PAZ|fAzQfqL?Kls#6_3 z8VwC&BBO7`!!7`6Rw>fs23I$IQW%ZB1iC$sVFL+cP%SNYDt~Vld17aJ!7cNGFAH6# z2fHKc{V&ev(Zx5u3+=6OGMYhV2glT)^uY>Ff-2%Vw@&7_u2UR3IaTR%;$<#h1OL5C zbv{%Sq*YelwBeuD3&B)=<3x2T!&oipD-4C|3=&~HgU=7z3H#=cP~Wz z1Oy9na7P7h#8L4p!vlg6yip9KI*zwzAZHqFv4-txU~6Yr_2E-244Op~@kvWFz`iFP zkUG8zj5^s-_pjCAkR$_JN7mMeXUW*9EyStaC<(?Rm|70VqKYGu{aop`)&dNP#+LR> z4pkTzfQ}V0$Jh~7_JlR)sWb!RVL%n^KoC3{`?-|UxYuxe_U7d3j()nJjpU^+NIgwDk~7B_4-f_A zg`=%oUw2O8)E^A9sW(@hUYI1AS6Vd z!e(mlw^lO^6AjhSWtliv0#-+v{n1l;vWF@re;P2d0@Se*v)x4VRWQ;T{t0959+m1g zTs3W+UH0kMic38AYJsF1#Mgvb7&9TSINF+e0anaP4^Frg6ooNrKTs?lpR=C1SXx;{ zr64VaUd%^O8GoMLZ}kRo&?fqWlcjzjL5bX&8og~(B_rpuiWgfK8<))!)a1R#lI1pl z1PVQyHZwgoa__7=?|)>Qu=ix^VP_TKqrr7iQ;k=ToGE!;>xRX3IrT!+2IJm6FJe{1 zGyUI<=-zCcf-EspTj2IZ3bF84o;lPASUDtUV0?f)K9I9&6d^~cJ6m%lIoKt2$$oBh zM#F?3dZqY)x}H~RMCCPMpE!g~&&;FOWh=#pkk{;-{z9lD^vHZ+QQzIF(E{p~{0+is&BTnu~ zGHSU`qz?@Z<7b+^h%&t=Np-C>w41aUOH!NFd2`XO{l%<%QdzC#{UtfSUGL=Ne}* zQ3MLq$PeC)&X<*e%>Coy$^3x*E{9*N%4*7n_b6(EvA}f~rkv$|OQ-eUfHbkW8BEtO z(=2EpYYMANf}GR~e7ArYsR|~KtH)_!AMOs1{l9RRnDX)^yp;qfL+*qO_1H^22^*dl zpT>=n+y@}`WMRaz0#@g4OzXStPa+yxvJlEnse_!1=Tb4k#qp^g;bVZ3%; zqk&qtyVnAr5~=blv6{}A?d?C5Dk)_>@*GjdThV+(d$Hb2yfk^IKY8`$?~=(bho8*K z8fYZ<4JdYQFV6nN@Aeq@0B`)QThz4!b?|S+46Y~ue?wS3)soH z3<&^@`&$REL!fMz96eVE800BhT5_P641}xi9`kzT&ECRi=a;mUQohxf%)~4rMbxbR zrZzVl7XuABJOrr`fZ;SGEct=H4o1zMTe+9t0Dz&0F%%iQbd@NvVa4`nHZwD0RvUrs zJ*kuv=csC(vrmHF@IWAu%`oU9XIJgFab^Gcjc^j{p`-liV|o-^w}G6Hf~Fl8;vxp9 zR*;wZ{zeFTXw-0-?pzz%wyQHch|nC!X+<90m6s_^x(-su7b3NR{UarQuO1Pyf)s-n-I6+w-)-rs4%uT9oMi5(K3Tue0ZX!ru> zsI`sDyOI*zal-(S%_Gzb6Q>V#+Qgis%q;V2z81@!PsXVE)DjlJFA)>+v2<{@FxR(~ zh0atiuj%mg^mh{g43(l|$T=6mtOMt*W6;Ib`SHhOkC{*7o(;1RRL>CBQ`BcYp2Zl7 zUfeMx5$}Nci4@)t*1!sjUL~0<3>Ov=5kStq341HvvSFqlaL}f{}T$R_w672>9kF);j|YX58TA$pT(c%t95WBJuF zSIgd+jDRa>bWpK=+NklXNr}dYvfmhe`!qFTZrVfa>hN!7aGu;rYqZC@Df%)=$#{!kJ$pnCn~<|0+H2gSQu3Lqx< zU+#3D06`JXp2DdOMD~9mfj5OQ+?1CXylp6G8JIJ|c{FHErt)IE)=j8d9iL0ZjKeJS zy9JfGP;f%{tbG5~PRhSHe{2H|c--Pg6Tgw#a^lxus2|&h)K;*Br2L^ad5JICF0Lsg!BHz@RbaEb^8G;gqGiLxn4nCjfHzf~GJxDrOv z?WP~!{S5^NMN~iJXVfeugtw%NE7HyBM)IyHjMb2N-q?BU+d9Vz4<8x2UNwrhH&6p; z#IGr}CCqc(zmJ+Jxc}|j0DTARofmRaPoVEC%M=W9lfcFaCV8MIq(KfKZdHuq=nv!?-x3^{QJu5#Zec3t_0L%xQqsMLXC6jnT`(r11x*H_p8y%3)p z4uNU9aT4S_roiZh?i2wIX~cWDGcXU?^8nX(YaAqQb4DocASm39EjHtcotP6q!G;T^ zRVsfx+X0xn_nL;B)qAd~g3a+IzOvqC6K|B&DBhB8?7Ymm&B zJen{gA$%QNg8h#)Ua(C#;PIfiKDr60m9$#ib}4U@nGS_sKyh`$dA8r~;P^B23rD>`uN5|> z4Hsq%wlN~{cU8M8ambPQ?XiGYZ{H?>gQEb+B@+pb$&U;X@x9OFN!DR{Ecvs@0~~rr ztXE+_O!|R>*WziMWVxLi$m^=)t8-DH_P-h-V5 zCA@k**>Lg`UEK&|dz+1I_s~DM2aeqy&I@T61E!BDh#(0bXABn0RS>_gD#+ANX08_c z(-((BgiZCs_0v?$c1xlOVzQjTv)h*6(^0`{AT^Z|p2p_y8f>^g`dyFp+%LsCDxCun zqaFjW-{^jUFo zJ~nD!V9-#AB|&TU+JR($xY8&Pj!RJps-OVB38PsPo;Se+opxOTm)0smX);KLJMpPKNFb5j$-ioo*8XrlWW`jk^V`25z=gU8uEObv=CaYD-?} zlwq}SwYn*P$p$yyW42rNk%6})!_Qx1ojb?xDk_K|Nw&7J2_Zq-DfhL~4kB1{1*!O1 z%C}0l6Dz(6HjM%D1XU>Qm#O;B(%WG;ye^YfQA$%bcU;&EE3jHF2mJKT51&kD66-+D z;TKB41fB}KPwSYVWeJ^Qc%SJXKoKy};KC}bVb=WvH5K-n!y2sSxp{u;PLbl zi(PrYlS1jj={aK84rOYZ4-;L3M$BMAX;J9c)ovFP0=*d@CI8%*s;_AYCWC1%3%`b(6TO}Ys zN)To*hgIk!NLiJ`11U}|XYOL4g~-LIZ~RLR<9C=0jMu5^&YPGQ^1H$xm^q$~6N9%R zzXU7wqtErLWWLlp5v;V)v9LmhN#HhRrn)H!8fvC<>=J;)8vVd_X|d2p?mZX2ZG>4gxs?zcy!j~ee#j|+dAE+CMI~iyJs6tChI>-V9BF+`o+&T=QpFi#8EE1 zvC2lWRKVCB(6%?7>IVP9dDJTVx$LXabo8Y7r06rViSRo<2Az{IG@WZsET))l&ft>n zy4!Y@e16YlqIR;ilRDqN(N6!0e(CjVpZm5$!O%wl$QqD=ZaCzCgg`^VvQ7nFaS(># zu|G#fYL-9EVG#LFJWhm#(#w-Az6vs_3sRJrY2hceUl+lM?Xzg003fL;jg2%Yh$<;QPZ1RR8K545(N%{%~zCDnb_8XLftZez2Iw?%ilL3+eS(BN*ha9Zn zva2an)`qrkKqNPmIx>XN?y=ePU$`Xl?&5ln$0N{huuGhI&>=xVm?*HHZK0{=S*jh? z44c|Qo~;0`0E<$-^_-TDZg0}=V?<#Rf%izhgggrCBb8=1w$(0KCjN!bv6?-8jMJv(qwDK@m!fG)Wj73Pei0BVECI3bp1!Z88u9 z=!G_siC)LEL1Vg4=#K)fZMucDEpFJgogv{mTwQ(S81T$9l~LjHlMQ2RF&onIy48s? z7K18i|BKCe(}VF8gJ#pbpkb>wv-0}_rT~8|O=+Kj z7k;aU;+~qSl>l%VN&`>F!3(xG)pile0p|AvG@OlenL*Ii{smW98Nh? zzw0HmJUQ6io3I$-^dZ$@43oKBOKSLcKDXokwl|fxJB3|6&xRH>4FQbi-UU-2f@dT0 zvREc^uPG)hL|$bi8(iBI4m)lxG@dNRIS<@9*jus6?(EjOB4za7l|H7W6`f2~0suT+ z%1;!ClVFBUkAa2?KBl-3qoCse8C|nVn-2y*Lk(`<(eSOZimrxr{0IJLcDqjlFI;!F zPL^xxy_yn}F%+AEZ81i1bGFYYh!;apr+WCfdCk?7Up>d?Q=9z8H${|A>FPa3=>S)O z2)=bPwpH(zL?a8}96=Wev}+=?q)#z~tOh{d1`C?BD53AukWJJ=8-gB^R6yyNpmLvR zGFgl`aa(g}kU8+UI9u^rT$37K__x}qcT~?_*;lvy(fh~MJD0?exBIiVZJ1G`AKw&5 zFq^*A#o&26Xq)~Gu!6}?b~Mq<_r^Vtub1EP_7^xwNXf~&b4dtw0@1Q(R{xg^zz9bT z_#{(>kzzq1E%0&Xp)*vv3xD3@8-E_VZX+VzYC(%NS22*Mc-H2PxfvVq)!wWqF^%;R zXJ|>>C7W6(o7&KQ0A8>j8X3_`gU%M<{%#g1`KHP^A>Y2;LtY>Hnvf4bD^Xnc@!m)T zKa13D(vp}X=Cn!_qT<@<2lRPrjQKPFh?~nj4=}mJBaJ^qpCtj<0!&E=ZopLdUW%V@ z+Of2k3xs>e`b=byhIuZ$uUuEYd&ZI5N1y0p9^b_N^&~v;xA_f&sh=bv==cIGJ^5dL z8)fJGr=NVVzYm}cUEP$-&E951KIE2P{2Y^h$BlzRX@Q&TK{&LWAe}_MP(1yM8>ZBW zgMEFtyguJ_n^??lP(uP3s)k==AI9S0Sbm#H4vk68HA+`u=yoG1%_mA|)^514uulK^ z`}g_U$ouJ~?)d-YlR$B?9L!FDRxwt@EqTB?kV*+~41tku|9`8%;_DJm{RoZ6O==RV zm3@MtI{+T1a3gkf3e=XIo5BrP5Ax5>J?3hrMu zp+N*&s_~$%uBkz`{;*g=fugXu`25k6CuU&stE#C9XK`eDPmi+wOw$dBDfSmsdB|Pq zG%JY(qv2nka- zY0rFd?;J1B_lwx=Zq^Jgq&{`TLiw{oH9y?T&>g8Uw@{tw*g2IXlkl<4AWWUF`DWRy zKRVcP$*{<|HX1_y)7-pK8RvbHmbKEG%e`?-9vy{TRe?W?{ypCd&mc|i`*MAu#FGg;By&tb)Cfj|jJD#UMD!_FYPe$^br@YY{ zbvtO6`C_kxv(f5U4jAtsH~3jrQM|tz$iRuPmmmVs0*Dh?T1r4Li~<-05Y&)w9tfjQ zC!4ZC+Nqumli@k1mx10FcQaaOY0JLNS9}aLdTsi|qBs`A6nAstX%tYS%b#76Du0fL zws9p7{{IxOFg3cEb$Y-ms_a^?hOZxSyIkS8W>ulOcjdqolskdD-AdT|)VVUTox8{7GDmwcl_mj+5lexDx5GxJY+Ka`>q!& zGS_m@6uLhOuOk**8JS83T^%sOmVKQd8kGn104$i1BoeYQ?J(8Ru3h8yUOzT{^pJqe z>}_;RHS1&vi_{&;g8@mpqhe~k6g+Y+_dHoDuGrZA%FQYZQhL_Ijthk;3ewDS`I>*| z`{aVbZ9%8{2V+l8X5OjK$H#SFq z#icEaAwqJuk1e!GGN-~R@Y(;Pncx&td>l@KOf`Xj z0fZI6?IDLC&l$wll8qWIlz+i)cooLT60oUf9f{jdwu>ozF&8w>DsqR^vxxjYXs)k=vF!WPQ3@(p1HfP#fiTjNW z67i|7Db(E58*SOn{;J@iEBCS6J96BcC7<<0?e2*O+h=hU(@tTY)j~`) zG`zaNsOBIa^k4Q9M7l3>zY11=Y`{n$JZxC!lnJxJdO~+6t7IPit@CPcfl{w6er9W{8tJhjK!+n7f^oEc=kxl2jQv6!e$e+5jpRIn^Jo{)y#%k zNV1C=I`@`UfBZFNHQJA%rq&d3roLhF8cT~jM=Qdfx00m2Gu|xOZRcqX=cnUCdQb7o z-s5IIX-Dsk#wfU-PFY@QJwBQrFv4=z=Vsfvi3sfcKoa1;=;=j6I|>;S!5JgGd%?uS zdSXKfP~&a_T{)1D&Sy#&ZlJH(yopWa10ezuNYdJR8X# z6!~c}adG@jIDWrUnI6-IC|aTYo%7|BKAFmSf$$W(S7y8S>zt9IYoWwf2|yOLZNrTHn-t(`r@0js2qL@4J5o&o2g#wUkF5#GJ65no&^McgN!g zQQ9`G9bx3sZ52kWav0FsRylP?6;FM_6*KhW4Ro2}oo@EK{U6Sk1ygY^z;?sa)A0aw z09eT+B}HFaDu|#Ea5w4&`w4knW&7_c0Wba^mgPQie>A)>6LDW?qFo-RW527^0JHC% zLW78^BRwK&Jc;{lTL=iR%@A1H64A%b9@aHn))|B11b^@Exg6rm?nF1rAH6G1ZE+LB zI?Z~wKP##m5gs{Q;1IIUEllQIQnY(7DS^Tkp>Oj4S7Z(BNzv>a`aXbi0=j!hA}92& z{h#DjE_*gVFfvL2Kq|cu7bF9L|JCl5C{w;SV-&TA+dc^MIx4ZI)Hth_&W9wbCyNLC zdw<VRmUIUWl7qAN)840>4jrAWGhn4O~D=YL_PWz7@o}B&&Qh?!%CB{=9F36iT5i*uIGJicKv-^4KiZlla$0WQZNBGx?jX}EGTwq$ z^s+DZ3W@JSUsI6e=SQA?c}UT@xVR9{KjR>$Zb5vqJU*Y5iv%aPu8wl5-YKeg$yC-_ zb{T4n*%luHFhQah?M9fqxm!xxQf#~WJN8XgEX|$^E<4ntktim5=-0U`*@1U8#jm{W zjhbmJB)mFzxI3J7nz5feYa{_8kRSI0>V0oXUA%n}hWla6mv#Xyg z2Md{>Hg;E5vcHVy1SHg~yoF44H)>ivh9!J_135|vbU_j-swjYeAlVZNk-NM%Dhx$H zHtbQmxvZn}iJxr={?v%gSK5kdJZ%>f*;6de-gDRs0 z*tK1t#pWZ?ixhL~)Y+|$HZ~#Y(?8_Xp1oVDKUBN_kz36-sJBN6u=bj4eicw< ze4XeUgl_52k8g+9ru?p+X3Rs^dRI8VqLBf%>O`d?=M}b z_&wa*2fkTfam0{x^d|S)*R*TVr1H4{1^)Q)1Kv6${|2TxU!jj?l3kv8#8C zMloV;b6qY^vZBjhCU%{~?^T9l&WtRGT~_vc-l_(F9D;tu4uTH5lljq>;*#Q?+5Lct zW;^1e3**~I4!^_fDq1se@$+&1ASvwL+6D%A;IRkPK6qP@g$yu{2!;@lI}iW-84mZm zIr#}c(i8!9A&QD&pM^E_9v|NcInt0r4?zLG4cr$GtBJ=&aDzQV0(G-SSOhH^mFZIS*#LTj5=bAe}yr{5}$hma>c$|9=y(P1?^SgGAy}WhU z@TmT88#6*mW3=f*?P}@AFS(BmAWW&HnCeNLCr?cM`vQs$7Yi{(jZ`;HVLRx3vb}*W zXL`C^2b^tyu~1OJK$95*NvC@CDjZaJR8v0761u=nf~p zqg7`_9%r}&wW!c0%-AOH9v+ioTL0T4l}}1+Ffsn$s$0A_YU89%F*5&FbWDt#q2Wys z!UcnQtoLyO9)s09K)H+}qmYj#8Y2gBK+U3;=R&no8R|0x%TGG6DfJZlisZrK}m>>1?vbHoQ>*Uz$xpUI#T|b`pQ~jV#^b-AZRcV zjo#I}11%FpVaZx{-brbG1|A!M)z_3#YI>EWq0y0a{nhfu>&@q6{Ay%?nn3FX7dDNq zt^zi=7H#;h`m3dY4!D6Jtxlv0;91H>kO^*hpWhU2^)uM2R{;G1gCg8^yk zNYcCQ4$q#$b4yEl{hA#4a;fwHCn0`RGY4PkQzzm+bs?^iSS}rwdv~}a^={M5$sqR5 ze8Tv33_t=z-Xn|JItpjC((_>9MV5{XXPfKhaNtSQ0iQG-{EJJPisG0FT)NX z{`=oy6kwk07*|pA1`?wF2!)!Q06$~)tUd4DZ>z*;0Tx4-`?P_h%0$Iv0m7ZG5F?_Z6eHD6iwG;q3H?C*s z%{Ql-u}7^2*2ko4mzxR2eUJ7o%vfR(Qm0dG>%B6a)mGP$hZ1F`I_B}tlO#)z1iEax zaf%3IS(f5>{a*N8VxCwZMo|k{eh;?aC;?M<&_ZL>*Vm8Qv_cM@XF4|F5*~VJQ!FRf z8@czf-Ac|?4Q4^FrjX=APk#SPVd^;OwjKv_#6Cve8uPTtY0RlQ>NaBN%?zMS7uQ3yCoL4 z_rWlvAoQw%DSuZ>ulB9wV(2aZPuIghlq!Y^7{+JqL`52%JzCchDO+=>IGVbS>SVyJ zvKcL0=*e{4c78Sm8X4r=2hXI-x5O()@|z2krb=DHN}ep^1m_T8I)E4`5J=mU}$k^)O7EOI@UMucp>RVw!Olkt2)mYwa3-mIat2D`RSqD z`*U6lJ^6sI8@qnL9BZw#Rt%T60?{|wCY=!QW3Q1_!vGoVk4Le1%LeS8u2 zNH@!WzmGoS8*#xiu}BhZwmZJI%ims~?Dz9aWF41D1V&Vmk3Qw*nk?n69omy^wvChB zut;S$dFdeT?vK&(Oy(wTqKPjn7p-Oxx<4fe?t99u_Rk1YIf~iItJEFPW!6lqYRS4) zJ^S%ZFpPawq)@6=X?`U$F`)F%Ky9~_f6YEY|3G|aQ5|qpbNwzA#ujvi?Y@vGLHXd$wQPm2f4BsUUnH6BlGyEaA|{Q% zKZgx+hvDLU**h^Qc`qR$!RYGJ^DuD6GoZ)KI+2s*E}0s>Q%5ih_WT~^(B^Qi;?oZa zx7g#OX+k360?~9|8n z1VoL`$3~F0ovGn#Y0ykp<0bVb^jL~LfB7)aj#kwEf-;szAr)0AHSa4~Phru!ygUZT z>w|?Tr9YUp$3)*ZaIYv`^>d1w1=zRAg2iM#@3YbrMKV!e9C~45aSB?d{mz5Gif>$* zD{dYWFbtj&L=SXzu$l{~X zhK39(fG2vg?zrs^%w3b*msBO=I!b;1uL1YuZ`t7P>ZR9WOWXj=Xd*#p1Sw)#?`T57 z+Sa<`Q~f1YhGV$ZdnNutui_{-#@54Wx50&OVwrH8jFX+S>*xFNed3p)jc;F)JLo9N z_Z~kn)~m*AwhuEunkT*IB7XL*+4&!0a_vn46W$=dq(e;HHXmpHt}j0vQNR~m{6cjU zG~jpZ*z|rz7VZoQNotH6za%+-YdUuOEKZGx%Y)x~@%>ar7|fw%QU9K3#pT`Kx{Es8 zK1N9v+ziH*bfOO7rfWmI=p{=dl>r-`O6Rj8CG zoo}66ro>mh0M^lVH&A9gv6fVb0gu-mxQArX{>++vg^J&>pM! z%lu|T0XAWe&p%OAKO~6rZjTw6Sh?o!ddz>Uxp>NfH+TFTaHO-e1>$Po6dk|SKpx+97xHCL6ib8|A0=^n4j!BKP~l zEiz!_Gq2=Vu>ZX|GNkw46W#oQ<D|WmEkc?5&7Pd4Fb4E6-t; zu@IQL>yZ{*))E`7iZ5i5zevuG_R99bAJxnZDL+UI;h5ySbHvI2NIk@;suTOeoL9<| z$A1sSU&tv6Yfp5;K8dzIfpNMF$F52YO=%ClROw4JHk_|1Kbw4UnCdGyT=%ndd@mF9 za6;%KFkzC*Y-Du7U3u%H?rOEIg8PXh<2M8j=}mscW&>OsNRyb^V?+Zgn&x&W;gB)-y#hMD_F5grrf z5)^7cu|sYkm|g7_)>uuAIJ1;-?+%IS%_7o=B%`y0X)9^R1D&_6@YzhD?y|Hh8N$>4 z?xQg*r56%x?BOII8smO3q+$Q%K~de}!zX&p6pqP^qWj78d5_{I-%P@KDPBCSRNaai zW=Ry#Qi(n?7yUG_A>C98;Y$@7yIW43-!tWMw6$W^UFx*AGhM%sML~GyafBlcB0S_z zxo~`XN@pu@Rgga{fR24NKE~zvamGL!9&GJZ$y3%}auXOUzW(Q9uXU-#n9-~A%RZgl`mq(gu$jZnWJExrb;4TYl&Up`Q?VgYf7}#Pwnf^zo)xzPBy+=Fie2x-R55FY^gFv)x&k&`+Q44_6iu(bNmt zAk%C3d9nOmXs~14I3PhIrwwzIfX~7yopgX}y$TCQ$aEn`SwjM;**%V)IcR z+8u_o$<=Z&gx1Iq-3lPd@BW@#m!*vGF_lY2Efc8%CO}y5-<^Pma_ba*nIiw?0`S?m zs0MtjOJ?h+m@v`f;8AmL&a%nn^%-_tj}{WaTQPE$W(eY$2ssC}hu*pI_HR=EUA2Kw zb^b5#i@tJ->M^1OMLoQfl?{a@y~dh514q~EX1W5-@_mCiuJT_(n2N<8BS<>?d26m8 zpFf(~{?}m}cV+X2x%1auDaF)3CZYMzT#}p>@EHzVk7oYPTyp@jvz}vry%Sgd`$^i} zgC+S+Mrq)NR~a6RhKf%HrUj?r=w`iodfxAGWpndT^%@X(i~)b}EqJoMM-m0hFev$i znbdF+^jjzCzL`h=`V+x^mR!5aL7I1DZ!_Aa`&1QWvzoB!hQGEEJLg^Ah)Lh++WX+2 zPJ=Y-LNS?X@(BJ;pYWeeQapYqVnXSotN*C}=d>eEN{^zHaq7Gj2danHgr6Pg)8A?ojtZeTtx-2`EGd&wr_cv+UQz`h^kDo^Kk&y)0 zjM9|X4w)D{PUDWmkS{v?Rl?diiu~kx7cv|)HpUh zQHvCqm^<+X{#kF;y-7BSAj9x%4(?`7CUL@~^N#GnnfO_^9{cNjb?{!?{AE1{+ganJ zd?(6U(NcYww(q$mb+9{N`)xe^*ybEl> z!Pyq4VE$F2+u-o-wx4IbdcE_wQwvJ4l%ILI-a-fZvES2=+}b+iVJ=HN7k|eYWK**2h$)F6@4L#*YPr?ujle!xpc{wN9eljQ}H%VT}7yP^1J-nB>`NACl7R62{^ z4_LcfHp8Ac2{u2}sZ(zlir&DXWUZZb%%Ru5pSLYz9Em}`+RjBJZZ0vv=%b7X7&f>& za%6Hhi!#_;H5gZKXdx_cwU_zKO2JtFjn_~=74HBj1#>mqx=Hr_??>eX0*mUe$Cta; zn=@Wn6w8+#dxJvFeyzv%8sB+gRvnYk9aY5Wu?$-aw@*Bep?yk0PA0;o*MUKrK$`!# zxQZ^BMaj4M9X5~ zE~f5S?7r)lqbwv* z1N!TnX(|F@i&wMbGjN#0N#v2gb>%RJ^vqp{|Kd2FCGQT!e0)qG^o-C!bnpas??dXr zdYS2ut84@_Y83j>-oLrq*5VjY2j9*q{LeoEC{diXu@3QlVLWo~Hl7c;ls zBSfh0{ib(e$i0srgm|Y!my%7Z2Gdag_cpO}_($b4zcJPg1+RVDIPx^6Fvv_VH2pz7 zRD^jY&HLSw6g2^hytewRS9bLBKK^bmd)5cz9pAc$a4^{ZpD!vOeW~+EZ-}*D?f$*( zmMezj%Usma?QFU8$lG7eroiex;302Kd!>&decGK;7_ojA6 z^GEIR|9d;~n;+x9U(GyIR!|5?`*x~=@__dJmUBd8{Yqrj<~ft7{SXBOwKd_G$YaEO z+xg_}nGr@a>eE%KBFiV`F0r zo;cj*Uw+^6DsBJavv2CB&z?QQVJ51pz`E%xUsK2+Y@zn~iytoD2nKehqOy;i-K%OK z`VKz}8e;kS9v4qECiW=5!K&n=2DuVx@^zHpP(NF>-lUkV)Zv2N=V|f2vqs1X}3z?I%_Xs&LLO;C8>!Hbi z<9n|^{@UR$Lmgc|==x`2r`Ip|3HRIf^Fq?aHFx|!QFnVa8ba<0jVH&N4T`ug|w=jDe5MScdt za%(gLiJV>*);cs-kN*3e#a{v3^DFn&Olg;GUuPIG`+iOmD@yvI<^38TNWV<}4`FW^ zRM)aajV6$U1ShzM;O_1o2=4Cg?(R;I;IMHB?(Xgq+%32V*!bpKx##BG@6~%%??;MC zu~^bQyL-x*W0daJ(K!C3e75J%TbZ|3Vb91k(KyjWjYAB?7_XsKI)6c~YzA|2#{YfC zYsqC6`4Cp2u%08HD_P&wuWn5eVlC;Eie-GXu#p)VE)N3pEE~N_N>jsf;PFPPvT$LA zDdEvV8~`A`q&ku?CxhMMOd_xJmfVPr%#25a&6?DFb~kUs+Pf279w7rj(et1A@7vP9 z3yW)7i+-xI-~{R)^TmXckIZz5nY8|}SfonzUKCJ~<^=+Q<`z`cRBH9xo^|F-PK#j# zkyISsqTDiL46iD^M*h8-{?L}4!$YcRt^G6Cd>O=lc{z<#i0zpp|3UNBu z7C3Jnn%MWUbu)DiA z@rpA5%58~Dx^ZBO32eDJZ6XMl5OYPz5~3~nzxRMEXH93T7~Hb^mJ}!WNA2Wgx3NK zJ|V4rc?BPe8afj^b6CYpb8?(MPf?Io;pQLyQDbEb7X6#n)@a>SiizU9sD{QnO?F!g zX0zAXYs+aH)AfD!30Ce~bfB@l1?eu)8}m>B_CGrZxyUv@B%6Lr6%N0X|qC z3y?FDa{&h_m~mg|)kDlsG7Y*6=?myS2<+RlUc9PWJW9M{vJ-RW3@f)vM;-78rhsOx&rk_A-ozXYlA^FhWY{j0TISi zS(%?dSyXj*FB{9I{3+2Pn{T|psR@&Oj=xtn*c|%NiM)PGSl50DX$ZtDJX?N6usmzV zzG=a~(_PPFUERFfstpzO$WkqLEI@Gg zllftE;?LJUrDAC8%;6{onrle2l8-o2u^$7WwDI7CA3SG*}FqR!U5+=C&ojcjsmVIG5~@0X&Wo z61JF#Nd5^0Q6JHGvbjE~rWtjf(2}!SS~sG^U`mKOC{1hUb-29Z$aP zziz_zlnLwG>Y7h=?<(ir+Q@J$Nl+@ym&b+2RAPTJjjQT5r?C83-#~lQC9=aP-L_np zp1L?LGInOnmJ%&}<;vypCApT?qZ(*=`hOc6rcl`WSn_jrH4{;t_EKX!Z5bk$cW-7x z*)hEua?iyU!8=5!m1p(Ed!*&Q4H6OmpKD`v@54k=5Xi);rg_Y!(X!3fRYmug{xPOM z7R8~I+Tj<}knX(L4W}!cQMeHKn-$yNFCi zE%w$({AZ5Z)m>CD04_8lfD^kf=uJRiAgLbk?pAk9v~}jUw)$xDhEYYO(mLz`n(-In)E+yqF;&~;X#Han;i z34UfG;sQbJCvz%B5?gv`-9n?Nt?PLG!f+hTe}?4=D6HMwI0KaZ_qwAKBirGFgya+y z#p>#ftth?UOlE|_gcInyvcHR&>n0$d#4xiKE{I^eOlGJ(>!6`?sI)dAr`HImGfEJe5500vk9qH7IC8VDXT_u{+drWrFJ8Q5lA}<} zL0GfW=FM4?t0HOq@MlvG>!2g9PXU2Jk&G6%jZ-j_pB*CcrD zsh6O7)`Gww4dOshIo!$je*fvJ8!SbANQRDd}-;C^5uKB;O-KQ4vM8O{2LH zy$Og!H2T3sG9xtCvK0>7+-xE1$&Yy$@|hlu_Wv&VWQB+=VzC`NEE=6VgnReaKLv5i zgpdah;FzohhknNj5%dr%JYfK~ny-)PPIqE=42XiNM@ckf*y$2~xHeNtPl>dp5=T zMRYu$2fU5b0@@MQ_C*C=lB5~oreG82j6|?Q<`7JgOf^n~#WSS+BMYZRJtnT`+lEM! zwZ`sl)m=i6x3doqJ!;sb6ZP)`k(X!Rz-S^B zbStUMi&|%P4@h@sc&o4;>`lLV`v%5bNVvR^2<%2QZ}+3n+*lWHObV_&w$gR52n0#a zDCY#414V&c5U0>zN9)?0S6LPj78Z~gj3^#5qaM>U{mcV#BZ3Y7Q5{0Bj7(rtqkpR6 zDHANFsg6w{5SQ{x|9KhDau#{xcm!4(;Hp9S`0=OVxo;;-JRh=P=TT!LuD7YVmSm$mvwF%0jV2y9F zbL1(?8S4v6TH>uaHKz-Qr}T*tZKiUs^u#6^q!W62KKBR-H}CGV6Dquhs~qfoaJDS2 zC@~V4*Ncw_1R)R)FTToHjt|^eQqU}ZwesSLir3Q22o8pdMDcD{xN z5UN!Z*rhPpegs}-5Z6(2p=)&fOw8B&DnQpHw~LkO4HESSq*$Y=&2ELmkKQ*c0L72A zvN3uADJe-&K7&=?fz0_NfGP!)6+f9Cs{qi~@@YY?_pj0lCC(bGIq=>+Gd%)KFh&b- zM2KlwCFIZTpnG+>jVXb0P%($AVSq+3i6Sfm@fxJ2Si+w4g%2x1-tvm-=N}R ziu>pgq_dS70{&R$aLk40O06c_C^>3FA;;1H&Ex9(B7E+invc8Z>i3h`I-3< zUxB~k1Cd8#Rv9^U0T!RbpV))TYTdWdQPQ&}!hT-elpsDY^v`lv)E;LX1g)BMeI{p5 zJ}E0Rsr;Ut3@D{^`IQNNtU~sd5q0MTm_4<;4tFVp_0!f(1V89@<(e<%yFlf9>_hf0 zIV)H$j#1zDC;KE!xe3sO_M4k#^|#$Z1RX?4rx_V*2t`4=c3AnQCN;e5p%K5Gat#f* z+f5xg@S->kpuS>w3){=Zv%dy=RAzy>sx?tqtOcAs3NTP{99DtAoMh5L zgk!#-sj3S^(CE%5UM~P+OlLOJXBq2klS!<@Z8=;wnO^8fRjbPnbj|EO5F@!$h>94Y zq3`aVt}g(55RnZ1WrP5pOhymP{g(9T(o4RxOc&plb3? z;VDaQxlD*fHHciB6v~qhhGSW z$a8bbO&KzY3Mn0%fk4JUWW~Hets72YCDT!|;COKB!+V*!TP?gdw1F-zs3^Xj7!Wk) zr%7}ab7J%Zn6=8k>gfhaP+tt|0ZVx38) zsi`s0Vq*jh@w!D;N{ZT%g(cu`B;`q6a{{=$lf{lqpiziBSbJrhmqjuVJl8Y@G5IwA zZR)i@oJ0)=g$(yIO$00~mlqu(kMCKo1^+jclnt|j63t3%85r(HTVgssod67>S2xb} zeFF`k8wQ}AhmA{)o|Gx~S%$T@r{{u&ZQDN7AbSeYj@_-eqO72zkYMdVfAS1Si<0aB zZh{AwSv5a1XQE&+^Ls_%yd{Cm#%k4b0NaWNa8rp2890>2u-4Yr{27{3cLfFJp{1HQ zz&FC?6}jeoez*np?NY21)>kvQQ?kL8P$Bd?Jqvby6=CrGQ)Zgoe@fEc?@7dl#yB9|Tzr(_{%oO~?|7Uq2K()3C+@33%v#ZNW8 z-G^Lsa6my`Ugm6G+6ajtLcb1Cs}2nXTf)i~5u@dq+u8vt>KQj&6BT_Fl| zk$yG;J<%4?_1CY&G&E9tZ-5vB@H;A$;LG%aQG=&x)TWbtV8#y?Kx^*YOI0=q!<0Wn zL$p#455MD$x$+|r?kLcAgcl}2o9_g6O&2BNr}{Gy`zqcZLHM18Me zHR_y8ca#tV@MPfOaio!MOlWmx`mB_P51!d2L-fBb*}hW#jt-F4D^$74hVm*lDyhrM z7>hRJ+2_iJhldaBI@CBmy3AKu?MT6a_VH zPFY?4*IQ>Iq8y;d>LWJxT!C?D(3^zRI59&*VsrB!zlDKF4vz#LHbil#^`8u0ZY97Om6Z7@2pLAb7TNyW7Re&Y8;xl*Hih#a zgMBAAd4d1t*S4p2-n?fEc<<2k%KAoq%lu9LzRy4DCcx8ua?6mfuwzN{u!W8;#Kr!z zsQv^kftEKVyEUjlHU(iqLl&Cg^iV}5I0>%&pWp!SkELwO$YZhDA=wp^htuPpSL{nK z)dK6B_f9eGmtqxZjL^GwPUZxsH%zxlns+vPQaT4F``7@f%32yfE35MSM(6?|8rpT< z#Ks!YOiL?du!U1=n|Q4aix+HmyXTvqj+;$zyodV@f2zEhaJSl~qTTbDaBf__O3cae zJo;qsiP-1YeQWD;)WIG`6yE*L#0#$>0z?(1G__3C%I`aL&(Fa9)izw6PGC(#af)r1USxQt=#n5&k#c7s_&S zQ+cDu)7kyX%qDhY(d;#M(d;RnqqO+%=zw%CFeGYE1GspOJJDLK-Ys4&{|q`{jrtb% z5$HTGJ(0XEQl_4$U~;=0Jj6ev>36Q*9y? zMz5Tjthe3&`n3nXtKo*%IFW_ZHO7K;^>4GUzcRx}{O0%f+V|7kXc!nwo?uNgy)M7k zW!kL`;>qVc$hhV3y&J2<40f+skkq6=bg%!Hv0+dGu%)_LzW!5Fau2uXq0=hQKEBMK zt?=K`wlF7&j~0GjCcrit9!rha|JS(i_Ii4Io3B(|8Gt7N4|Mhv8EkOEsXLhXWY7^OHemeIJ~N9V-EbfYfG-v^VwMd3iIzpL1yKnK*D9l;eYMLhd0ff@sCaLIGTjMh9lgtE7p5USC?cj`M<9=0tH}X8f(&IaV8i7 zNJHg>Z{WN12(xilmw|uhl*psn`(Qx)3P>|^s>THl~k`@-w{ zU|qBG_j(|8Io5RfsN^%F4sAzq655l~^$NZhqU!GtY&$VqzKep<-Zlieo@Tv&|Kk0$ zxHIEQ&*Fyhzh)tX_l0Sq`0wNpAxeC#`}aS6-3!4ccoSJV4fAQ+xlpjTX9YkP(9E@q zwY!F48d)JFAl)_yFRTyLPbinL|2t;(0!P}2Jv}{NBqfN581KbPeQ(UBdX6u@)jFd( z*VpHpg30Ol{5LoCxQ?^|98MCwrj)QZbYKV~DJSRmZ&PxXp=Q?3*d@^O7(Ap(q2fMfHcr0f$PV|dvK5&nE$-8>5$ z3(zGYwLXClZ2j@PX73S;YWLYI5)e5v*R)+W)0dN4-zIjW04&p6z($8n-JuKM%zqUt z#`H4a>6vO}vr1Cd+2BOYbt;m|O_Sg8U-?aPQ*fkQkNM;mm{vL5Gety3e36t%$^@W6 z|M~P&pmU%pH!nOYA~(M&%(_`~cm(K7b1y*aCL+o&H2#*DF;a+Nxs=rU{RGgYeuy1d zL)5rqYb>iv0dg_}c#Pe!e06dP8j@aGTD1${U+0A#6)~tXTyuzWDdPbjJN$xuliw)m zqzBWIgxR}U?qsWi_e(wu0dT-F1YyRrp-Y+vu;tP5(FX*$glCHXzEh@B`!;Gk1=XUk zvoaG(oP7BRu(!l#e&2lo#Qzwa?!Ks0YRIUYf3Uy&z}u;hZ=~tO-PnuBET^F$q+#;n z0*Z_N#zqPB*@irdx7PyqZXQ9oNc8u7h2MHN+0r>=C*``4PRYE-UcaI5`cE7C7&}ljQ_xs3BvcOY3)Mu>}I{PqLt2i5N=0jOvnu+?B!sdacYYqMJu1Ql175>ql_S1!zmDI#BlYSe*YD9~OD zrgZ6UdWL`Bpx?bqkO9oIaBaUmW?p)U)$b?H44+ONuQYXVPFI9GtFbN#33*(4t>{Dj zU&En;MSv^HZ)#dQtTh=o`2A~m910Vta-0lrc)vOhu1mF^HWY+=+Y}Ix*1mV+p$@?B z06YfJA=Y@*Hp3zv5D<9j!D}}21`X#p+4*?EneW7CBPPbS|v>+%N< zibs75>ivO8q0+8BA!IzXfWhPM*?(80Q(W&>)zR`PQ1CPY4)r?`i<|dP>!((#=*gY0iYk0#@^r%|DS2m#|x!# zCq$?#x3CaUC&U6&A(v@pRJuO%+maLI5Q&J0;$K_4xG>-y@qLw&0T^7uGPxbXqoaY< zm8*`M(hx=9N@D4w)?I==`bB#36Ca;{_NE?u>!G+FD#~;ti}w+j837^sCJ_z&8wex| zFaw$&rp6opodA5G8>z81?%{z8aO;6a<|8KpAOU7A%?fm+1NJW=CG{ZRu(p%{#|$5s zpKoMgSpu{u=C+lgtqI2F^8pHRfGW76hJuvOI$fJiC7yDwG-7BV;Aa4Jejp2FvKKGDrU668c0}=cC5tt2p^-hfgPYB_ps)A6^5+<)NVySz+P!lm8ZDZ`*~g8H!Gz zh(tb>vlnz zT(jk1bwW6AYfF`P)gBlMnS<=-kp%tR2g-vy1hUCoR?pQG1$G=69NHB+x=TMbYAqKK zfwIjo4oeD9zW<>YLBLr798AL-?UCBtjR{M(et4>XzT?25y#6HAmtp5zx2n32#A5SO zi*;8cM%u-NsoZ-#qj~jl5eob!=4BIW=({ZcK=7eOXVMbpktn~)+M(acJ*I6-*7=74 z^V^gh1DQQ`*^F^?b_60y6+vT^=5#QF9CY;SNm0;GIbB(IbxMHyI=`V1XNJd(+)P8v zWI^_0Q9{_zm(RqXfrBECojsq5mU3X%DUR2SLPL|h03bXnc=COu6Vo_0!OZPC%yqTA zfBWn4#JduP0Jp=OC?^LVhbf9SeO^&TLrBXj;N%NnLfZ@~?MJKxKII`_eP5_$!W6Tq z)qejxcN@KRf(mdb3Jcn&d-KbZ$iftg=_u$$XIz3XmJ<5PW_oLjxo`w z_B32Fo}O!VOs1!b9edjJUQJDL#{1hHPa3_~dVAIek%sX6pl^PoAA~3Qg3DwvE`{&0 zGc*<A`@4?9~ywTi}i2|D<77eK++k-Jws+N7)NM7Z$hT+-AOLk$d) z@o|zgkak4|eVZjF-^eMRKDA+e=jhp40&~W#%NBBg>oxrXW}@ce!T|g-^TZAXz&i-* z1;LtLe`mM9CZ@j}2*By#o76u+h;G=yqEEand;IwP$ z5Qq8+srSZz@Ac*#P2Ie8Qk*!UmzgxsMS2yL(X}H+2_^e!Ei?e5#ReB7?Hk4I>jTZ- z4l(gz-SMnfWpBXR9_ zU_+lD^$zE_&>*mVbT~P}aVgjQYGc0R&#U73JHzb!>2%n#52C}4xaZ(=GP%XDQ5Z6Q z<$hXF>46HP?-5r$eSJPaCRN0CVcWKXTA@?Re$cK!i$+$*;Mb0V26{ht44CFg-dpL=^A^~*)DBI zS7J}$=*g8B#~J*gsx=s#XOPzM%83%hdrz&Su`V!R?5nq2zr?bUD+Z=X9n84jsPfPx zVx@{tEMv@YKONWlRv%&N3not*klr5dT<>DYOYEoT8FJJ`PjEY0<8gjOnWc3HHST+l zmz?&`Vxy4U5O!ZCuXnMElV-1$rnqm6des+Y9EnNGiSS_e#%zfCW7vv!H^giOcdiRUAFiYKJs_S`LDwL8K6@(0`( zn>)hECo7x{@{9>y*|m{AthY_-5Bs=xz3hS+_e1bD_z>vIrz3hVd8I&cT%fZ#KAtsg zXR$`G|#q5G`mP)S%elG|umSSxH#K zc_2lQ0cH;2Mw~MFA-AodC=m^J457DQ*W&Yn(4TPJB1Mh8=e;<7obKd^005olV=Ho! z#?JT%I-+uF@Waoc4zt*p4v@Y&mHkS(7ee0_SFTY@w9BHk@!9kNg5^T5QfAgLrbhA>R3aK!@@|?UF>vSka!s&9q zeM%|~4HY)sSF~f?X#0)1S5BMXRuUtY5hT(V$7c6NJky7OpOdLz>G2{H1fta*1ov39 zReXtm7xn9ancRL|{_sv#_7+yB!y0E}{;lJacICzB2e5UaDMID*k$Ok59n)pZ;VNr& zmj^v}ARsx$yXrfv=f$r#PoFW6n4Gi|&Kn2=;Yp1f(1CEDVy|mzO4hHu>6Nf^gC9-} z#CAM6%y9fEX8_*Pn!dTGdA#ksbGy`dY!``(XN@>SmQ_; zMR}}b4D}sbiSrOq(?^9R{NGX|_YkAH^ zbTG7tN{KR}F~5R7ylkEF)9Vch58i=Hb=Ak)eD@p%iiy7@$8aDKn!Ls3yN9{z(4MTw zmE_6!5yi0uRnc-lp>TWkd3WL^-u(6@ZaIDL@?eoW1x9ey>u z9h|%X#A%BABt#5wuqP~F*~&>fiHeu{zog$-dB1S#+?9O$wzXhCNsC&1e@98+UgDdMQ#(_;?fk&JH*npgPh?Hpv(}TarXtsMc+!73)fpR*?(dUs3w9hp z8knA7xAyL~-~-(vLF(;b<_ZVG_`6n&jqLU^J9owc>j@FT4~Pe*8nVM@YiLQS9Kyum z;c`uuBa8JmMBEFNq%Yv2re`33aNKtCw9Q{uQcs6d5DDq+i4=46XtQue9P>I7<+HU% zL_1d-6x#nn*QBSVxsmwm`BoHiU5R%TO(K)>+p+?O08yJ0m@_kY6Z~3R8DBnWI98YmAH}U!Utw)=S&LH68b3LX&>|LZ1uOcEe3ShllGKD z=xA!8(RF2aI+k!RTvu#cemE96945%xNtoQ;if&=bIy@?_aM%u5YJrS5b-1EI5Qt-m z_X3wIE$au>i{BeQ-Bm}dZMQ$EpB&{8jo07T$aKVc`g+qr2l_4Fv_0SH-nESdQr{Sy z`EGhWw60oZI&%k-$)4Ap{o$lIwr!hsLUP-Q2G$)6sabWWjqlBEIW)R_k==`B(V{P@ zLr?jnUBI)dSl1PQ$7f8x-mkp4`+Vbd^}8wp^%qE2ZOpFA6ZaPsmp6nLoCO9w;a_I-(GI zH^1dQK>NcM?S0uWf+aENu$GxTvted*2(sNveK|ES^8$2t=6ahiaFMu86q{)dPEbJ8@;(lHm^Y zJnQ)OI(mcA`i!asFEzvag?QD?hz7B%j5hBlN;s?=Ar)B7#>HAn}ZGa?kCTR5qi=7ZVaEo^Lae-iWNmi9Z~!;?^flp7jINA5M4*Yid%dTFG5HJwDj(K2 z736d_L6%Q7wHqW>Wf-F{eI7jjy>3c9MC*(i&yf;tsyl&RWQ>|4pz7N@yJA{3GPYg9 z&olDYJnhC7LMZF4A=&ri_F{Y7(9Xm4_k9$nS88o~&rhnOu}=mwp^n#_$qvJp&t|oJ zMoHD+Ue30wdny+ds@%uu-6-nH-KRLl`SEm{{Oala&Ah?8L-AKJg}Ot}qZFI9$%U6- z75bP@l zUns5ZbBN^zl%0BKJ|QY{wM{dh_Q=o9Nj*r3Hd>O(i~vLRbL{3SNM};^U9j`d>p7#z zefo<*--?{#zLukFuek3tFBR|I=G^q~$fbs)wJ z#Py;Iy*YkUM&Dr!7j)$ z7Rm0O&drrQT2Q_%lKHHxTzyUJxzba^ar|)1`4Yw=RN`7?1fenUO&^Sj)!kWRly#gr zcz!IN=UmZB zrdOK_F_k9F$AQMbl2|swr1YPZJGUB8QOga>Yp<&3l6{asov3ijZX21}=ir+*oBfvQ z0!u9>F;F==YIq~p1^7DmO_&`p*b%di3D}4DU zyakS$k(hT@O<4K1Wt-Cvi>{s8C%UsE3HI(I!Om&gpC73pcYDg#Ps{94AnC_rAKF^t zY4VpeRT;3hxTMm7(@S89CVPUo={_p@bREq?pDwd&cjf7h9r?@C_!V1xlyc5(Q!a>4`@EmY!GtHN#RQ4>Kkp-lUkw;6Po=o!> z{fD9-QkkpvYm@-bZnCb8Jrcl-vFdSL4ntyhxx<2l(jG#m+4n>bKjC%Y5p!<37Qh)l z=rBxAXFTeCb?>w?t#x)Uz7Wczu$3Rx8Bb0xZMtMjUQ5P+)dI1^;FR62-uY<9u@wau zyEA9LQRO~D5#q#PZR9ELE+8rBrG*E;D|5bc>UHjUo)I4E@jGU$yFIg3v^?PC=H;a_ z8N&j+!1}}Sq+?mU=>U4EQ2LP>Ahlr!nth(0&wW=ao&VHceIcD{jXwAQtt1DYObFtz z!D@<6+v6?F-J!JX&ZV0j7dPK-dckR9lsZe<-uPhArFr;=BTX;_{MN=+@_QGJc0Y{m zJhDo{V2N>kkV)pCJv81t)6v4kZqH|>PU`0Cv@cZ2Z389r7zz&CoFo(%RvN=cRCY1V zexrr{o)l)PIX@$KzY(W zn`C9^DNl?n_2rwt>5hX>G1S7Mgcc*+J7H)syd8Q_-w=Ia?L#QKl?&l0$J&5$dV?8lxRms8fn``qFHfbo9Eankvz ze4fi>`h;T!ee}-2omAs3^HI%8^I4Evs~WPz(#|DP{O-8vZt_=kU6_e)6rTfei&=ZZ zRGiB|S1glWZMf`+LPFZJC+ef7%#AheM??f2W&#Tx2V(|om1^QP_-}&6QAKGW3NLVl7oSDBZ{JlyZu~X6Zy_A`ywd0xmoaYuV8i~KhSe?#Sl=?DJ_riDI z$lz7?G_8*`TRf-FPf?}s#*PXn3RzEA$x1GAheyj?tTwW5^2pZuL@(3U;P$U#F|&HT zUv{3X+%yqTaTc#9l(Jsv{I7gHFxmU&s`hkke>a*tesKev3Po8f%w9*tFF*bU)D-CVWidMDV>*{t)@AxIt zgV|7+>nZ+|$IuTg@0I4h5c84wm-No(9rV5H7vjzgtdXf{VSDe!tms|Lc1Xtze!iw+$pw95X2;%iUbowjvPiTdFLDY``_S^r$rcp zYU}U<1~a;)6Mnln^bVdt_Tc8V?T8HnG_#-u;X9vXY`EqsnvezWibWn8tLIDRYTaE% zxgy5ii|IJCcXBa9FLY$cTAwUr54mjayCAGwB?R7q}1roMr5oxDVlf-SAK4*Z=$G3)MVV54) zT&>y->;|Pva277-dOqjIJY(-8!h5PKdHlr!EKQc`n(hajhmeBkbv*oEa7-P~t&7-@ z)Lxma`?~FpEB9&th9e+Aja07=U3Sy8D@(XyJQ(EgZM8dH+P`Wkzj3+2SwI~%C)t1g z!Bd;(m+anp zZRB&ck{71zWJ#f>U7x55smW3#eB$&Z^l2VXulzY##N4_i1mPL(unp%5xT3!nWe;H> zS^71qLG3sJpmpo(S<-H1=dn5Pm1#V7@Nwwf3ujssT~sh8r8Ad(nr&t?!Hj151*Fodz42s>xf3yvY*5&oGiuThLFK{Mr2sn;p03tM0$gx>cbm`v!+F!q zyrw&#Ip%)5=6e4tK^9O^F$Q#HR)K8}V2ddII?kc-0D0Uk)2U4}1!YPC%=u>n7Imgb zf-xT%;IKJA2al&W0wkdqF}9u0oqm>&orSIyxL+xL{5}n~kd!={dQDsF+%MMgG(|xA zg|dgY08PaWJHtq>_J}$u2^XH@R*eZ&nA7KOL#_4964JO&Lnp;-R$P`XBdlfswI$iu zTInpzI!Dp-5#}3#b@sINMCsQ~!+B~@E%~XGO)JEV+U<2pFyZg6?}W=MX2_gRpC@pS zb0Mu%I4mM0G&(pWsn$AmrAVx(T0`rtxZb>;b zo(B*^O5uv6oy#doWH_JKYO(iq?J(LW`VNmxUfGwr#kAkJuDWQU1;1nt5ZhLe0aWcK zyXu`rpq4geiHys1(`Fkx-t>)UXjz}8=x9QpzO)(N^|G35OO=cBm$RG=QCWv8b#tt) zupG+SecrwR-^yRB+++{Tjv4a9fG~93437v3_txi%cRZF!&I$|XM_dTo1E9g+BDKzM zG1#g8+%K>B?#_|4*q#pjqKG`3=QggUF^!|Lo{*EJqC{RplVM)<+U`{w%@_?Js1JAY zFG=qKj~s0_AKwV|UV@L1AYvwV2c2OG{S09?F~UF;W8i5BV~wRVnq#9kR}pW zSXb4@LY}DTN4mV3_ZkZie2?$^rWen)#zC6YGy^Z%VZX*a-QeQ8@cW|^@0jW&!ZgkZ zd*WIGkD28%rj%c@zQmMzHrmf!(vlu_NF8{;Ko3z{~fh(S1cX#VOMB9e#(6#g@;^li*C&iM0*!I^PT|>)n8ejXGCOVA#H6u0R1X+2t2kBpeq9K zYyxtZA&wKAp`{fq5ikT^egK)DDQJPwV)o0r`>i(68T6XID8qq@TBX8dC4~KpgbTBc z5gHKt9-*@z6C6Ae=cbw zn=Vmi=`VR00CJ{vJV`cpFtV};JJXMoq^vwWS&fVQmFl`fd&;Fl<=DcR<{x*{`B@T( z2gR#)TB8%+#O^NZN*iQ<-H8b!Q4^Hbzq&ad5rVZ83>Nvg#lq-4n6ePK!orB2V#BVp zF}KK4?jv{-`11>IBuKYCW1|B?bmf{Rz@Pz>*v<7$`t1P~EUcvfKzw<|Wfl6iK7?;> zJm5sqypC-O2M=*A*FDSz9lqM;$?@(Y_BH-(wCyv(FDEX$^F_%D&Ai+JyWf3ocmhY4 zgN)bZtTdHt1M^<=;-|8%j8qxjrWt*ku?^K79^5Oi(gSbvozpxUs`D6QKJ+G}Gm+y_ z!V}_*@~c`K#=3r9;>Ek%ICsVwhQ~$|-dV;+bBi_6i)7kc&pfdz&byn_WxTI1OvkMp z{8ZeADC!`Si7-dmTNhhx%2JlA(0M!3%A_}&hp(p`6xb5aja{411HWwFQQkUSeVDfkf7w`f9~76j@qT(7OClk8 z{qfTW)*Rsj{6h!yZU+=JxUepCx$5j(J^DpzH}pqz^s2UOwXT4-aDbxE=O8kLH~BD7 zBqU3j`_G2138&o5+_+F@&7k(U%q7z-_an~Eeb)*5r0?WC=athGfk_(I*!(fHw=#y8 zv9^~3mrmpT-;35!+%5`8ynEWQ*5-+_IW&Im&xGp@7>A< zs9wu)&D+T2iyc9(ckLStuNUTIEeDA^ zX;lv>`1PPa@%4M_`n9R1SKQoijJwZMI+>&Zx}e6~4F?`wG0 zN`2@cO&*Win4|ptT&)_6y7xTqFK=C%TO4}td}prYEiYAxorBq~r&mn_T0Hku zMYscO07ehKmEt=;tdgTN+lXx_I;Bdy4M{O;CYk`~n9kwRQB%7$oQHXM1a>%4d+G{A zNqjCBrc(6Mizs6Al1vk|4@~e6lE{)~hB8V;gLjnhHV59ZLIyWReRSAYuILeO;o`sa z>a5*UzIgkfjHIgCjSq<_q559kd}Y2XD9c9Ux;7gvQ%$Zt`ht{JrahSBp>3`y+_m-o zd{|%mNHOoq#=gH!K;dph>dfs37hDO6H*WQ0Y^oU9J5R0|{ZOMvIT@vW^hki)X?S=B zfv+gdo*m0g`n-6)D!u+7tff|I5~N=~wfXendW|!kUG+?Vm|E6Qwk7h#sn|r1uX(#{ zb-@{^IdUhk4kA^V_v+yB)_y_GHkuvAfIZTFZ9+>Z72LinRrW zpGB|9+x&!`!FG7oLHig?=$?pi3=YKaiC`^8$o~l#F6Yr4Ha+?ozyEeb{coEo zk)Wj2rgnrtoJx8Y83`(zw*Q5LTw`1|8EGyEQk!=rXV+4`m^q&|w_i+)V3GT5^*QIZ zCuzTS35!1ajF<0O$mheRaL2zJVu>^#IdD?uuZ-cmj>Wnfu^iQjih@NkcwO&0s-yp< z(e*Dbl}Jvo;r%!Gx!z?=?U8PRE#zd!@|EmAaESXyeFYSAcuzjcT@O%m+I^gtr2wU@ z{cbslJ*U#l>N^{buX&BzpZtS<4?e~fErYodsT}y~6+TWG&a{lLxp!7GUKw36wv*5Z zWGImfDn9}cK1GYD5flB0)o;vscowTp#HT`~F;midF!)TBFJF0+r8|z{L~>%^Z@mBF z`~1~@h(EhDLCADUUiUkj(itL~F= zEgxynFRNbNy3Bg(O(svC%=GEgnK*GGefsnvH#e7Yt~`f0|Fy^4PQ`Wfp7 zAxKI}qTuijP!H0vZ6;@(nak0md!Xn5?VDKm{kO>5n#(YA`Kw7OL8`7(ap^Lyy+24- zn@G1Yr|w_Q+s{A8^vCaI$}1}=B9+$dayY(fIeEo`jC1c}_B(I$?mKVu?wlvNtf2+x z?iDQGRtnY6V8}W3u{n0K=<$hMJ8?3TC*8obUet*csUj2G(5FjvIKGuV?y zX>{+|iUgl$x!X)+G2qHUWLrhqg73LU9{!oxk3Yf#lW*Xrr@m&lGnKZNTtfGh!1k`3 zA9w$IjTPT9;*JR;RaZb%H+#psKSg>cdbLVI$YR#bo54Fj?VzC4GX^!7jt!d7$R?0S z*YMqngE(>U_tIr-a7aW_HjV07z+RPVNdnY;Y~FkrHG*Q`bV|UeQi5*(ZziDvIm6^$;7@?7Klv@0PCP*VVhJ+qo09mB{aDymF-!A6FV5ieoesnENy+TT=pijgMCHKubNFzxYr_`RyU@3NimPsOXE1x& zRt^=O;Mo36eDl8-_;|Y$sie`hPg_?!3rigu)U_gTu>PwBtl3{gap7K8&;Noo#S&qu zLw23uZJpR+C|8|R2Wv#7Qc_y5gO%?;$E!c&qY4}Kd-aTb0&mZyN9U^KFZ+bWe>*gd zx1e(+)LbaDNBeXAonvWtWFgPKu$VoeZWM`RwmFNDlON{xfmPYE=vy{9w2xCy9V=q~ zn!etJc&_f}L{W~CS(hy3Ae;X<7!)LkbX{*h=<5Fv{S|rr3qO8OZIKk@eaO>OuH!#r z#&XT%rDl+x%d5z zI5gI_bla!QUY@~4Bf693*G0zcA`6X2zQFts-eu05Im~(UJ_cqr;^K!rfCb>(%CBpj zwC>>NDpZ=sTTgyX&dpO9nVTG(v3`6dZ_GNxdE*CA%UMFHJt3yHlQ4iGL>Y)P2>(Jo z$ggu}^3Hb~IZ&#QkXn!4qi*K1oK3v<)-F0vxPYccl{rT~e{J1Q_9@7kc5a-_Est-e z|I~XK?2i8+_kY2I_iSRov>WJt%BGi2q~&E(xao_zeE8x$%sOxsXHso)dtS+1L%LBz zfOFRhb5b59c4FM!7x3s4S8>4;f?6GibNA?0uG5lm*Fr6Ss>JC=3%73D#>|;-1l%)b z%xD_sTq|zO6!2@FWEnF^FfF#m0 zs$t)=lV6wZ#9~h-y-s@u4Y-(#yTH#+ucug4r(FlH69kb-`wn%OyLC4|uh_vwIXN`H z^bYP!o5u&=tYY(?UF=k;405UULY0avE7}`q6XHU6x?Ka}$9wO4|edP##-SIjr4{c(>fA3_Wa8c2xX%Kw6iXQMt2D6cO5lItmBzQyVkQNuY`n> z{jB@?0~YSj;lgX$5iIe;@{1J+Zax`mpyu^~dfAHCid-%*}@Ytz) z<`^z(?>@N`>GU|i7oTqWmVJLN=T$a$yn%J zA7a}%XVN|=okaB*`?halU&bIV@8LV%+GqIBl`wO`%LUw0t0&`c+{;6cyv+-rG~(_H zb9~}xsC$3q>-CcQ4bn+)93pSmaY#-jMQE6hesiLIRt)BE;~1}ZN~m>1=X0Vdr)p=; z>h=NC=fB9TgbV3fN3d`8a&|bO6(Ay9M{hp}t^FUmzuHGy{{rfiNdA)NxOdhbdQWrw(PA<2fttz>R&7QdDlVqJ#!ToJ>v!d+KlI=`v-AjU$cjb1fDv`30>ezTodkq-J|-%AH*VsnvWG?USJMCbZlnq*+Om4WJo)rXRHg9Tv}iGyGl#V1n%i4M9;wnD)1u5h#22{mtOe4|$Phw+wKXFje~ytM%IrPq6?&I3 zIm#8$`a>V)t)ZR^PE?Xz~9#!Je4;Fd)*_g)aiCH6T4j;WRZ53>(~kP={M#M`i=2#Jg4hKC&;rPfp%l3 zF>9=6MQFq5r(a}r&`xl2s^GF)w{GKqug?tle(Y6aXxOkJ2!U`1;gl*!S6)0H_YeR} zW?yc6FpI^Xe9CvfZf4iPqZCUkNvSoco!yMKb;2Osq6)1?+`+BY-eKP2-`IZ0L1IP= zIt>`c<>xeV8wyFZ8*wwE#oK(katr$p7hp-OMrNJ5G;f^ltAq$UH8QGFvi|_2WYMPQ zP)1*t8^qYR)8yjYm}H;B|9$czj~76-2JIPq>j*Ar9IMPP8lEYVsn@a@)qYvPe;+@N zLrEc{Nk@iHxs(Ap)(|>E61l^ta7*gDy#L*7rhiZbN$E7`b{SoJK@y3yynG6iQr>0W zH#3We~DYa%JtYSTC~+2=$myZS^ zi?n@vIb4{}!9oQ~LJBprTaeqOKNk$_LZ&#zk4sl`R0_%7%@59J z>tCPo>ig|^aD>76WNXB^Lpt;A^FL9r=`$AQ_2oaU6H#h(`%v|6x$#Va?k{lGDt z)>JZba_MsBd7R%Pn{p2bK}!44+ zSKo7@DMzHxarCWRmGCy7z5hI4AGed$xCU0?W=lo9uD2i1*#BYrt4Dzvb}eeC^U19xfD~F(({YeE<~e4M33c`I z=u^R3+i=6%uljzMJb*_(9}s9#H5$e1-;4@pq0s!Q*Lc$l3_NSfi!rmELa{6&_SC~| zu9RNqW!~f2D|dh_J25LWX#D=(^^+^=%<(1DxbcHDZhmMKt-bvC)V&M2f69FJRZGDx zQt5f^{ao5$J?}pG2H)>GhC?LN{n|(QPdgij*M7vzIp47H2qb6ba`uQTIj2b#DEOAg zZ&^jJ|K7qG$A9FdCl-)=#bidcOC^8x`@H^tKeFveF_xNbxp3?l&a7{bDy@uphmlA5 z?x{&EZ8?&R#ov%|%RT(Nu1ek;xAA83L>?KI&512vGiS~s*6u%!BdIC>zV=!MHcS9n zz^b>O=e=L{a;&fjHN6ufCXS_7rb6Mi#k@27Q&#Odj+)w%i*KC3zv`VDS!|06kMq-y zOX>Ts0aUG8t-_!G%g@WnZPkXt6F_1fIL zf^vE-L_!E8#Dm`alf=tqaO>7>%z67AQc_YlcI+4}ep`4EISh|VX zcT8vhkVmVleH0T0)u!Nwmw57rrcAo)T}Y@x7f6Z(x9$7LOB#AMGr*>+Jy z*iP%utb-!JB0xd7{t8#D3eR8Rj*q5zshb{lhBcC2Dm7g9RtAzg4`#^H={z%e8=ZRh z{r`$|J3tlax4BUkYmd`t_K6+azo@TfjAEB@c|4nwmvuEyj!)MNUq3xm$?W4x*}R zd1jILm^n%PR>Z#TJK6WgB32wop-tnA(+sdEVgL4>6v-28Su~69lLl}{w>V%Cm$<|w zm^j(F;wv`GEHcxQQ3`kR%Yx6@nbC*Q*%l4g!H`tDx(f5rMvlQ?o6h?kjTS# z4xr|VA_@=u!fN#f?w!#Bb@iJ({OTMUK5+wQr2cbvm1u3JU@Qx*j0+vd@@{3*56lPv zHWC|g{*-6v_2(*tV==T0J ze)+>mhaAAUmHAKpLCt~pFr|A z$RE}w^sa5Cc1k}oANZElXSZbIz8rcqf=%;3V}IB2Olngd3zGDj)sWj<6mjRa4g8VX zoIy1Oh$ND$Sg_h)^A`*GtKF4M>{g579Y3;cgQUT*nx~Wkswj%DZ+k`S@2W&~20q`S zSb~0ER9J}BYCEkN1*+@YzPStd-r~U-r#uw9{;H$zKw7g&(7q_;bUhn7J&63w1;e)@dWVGnZ z)e|qEp*7y^afwS@g6&^L?Em9uKK*eg2MUUiiRsjD*^leT4WX$`!}Yk7i@hlqj_ksn zPpqcRt=G^j!Gj5yXM@SZOZnf7pQw4&lf2#~jbm%x;i>5_Q}dx4=|SjgJ*$hbphe>j zo+I&0{yl0BzuvKmwMWjTpWTj4G^EeDZAiBZ=+u`^@6KTT4ku@{ip|DyvhAW++fL=F zJbY7o_<$KA96S~Tn9v4cl%JsmKEO)#MqN3tQD?f=d6>zcEaChcYzR%!;3M3i)TC8c z8^103fS)>EL(dFXfIN})FK&K#C=Du|)h;=-c@sw(olTAvMD@<}ZuBAF%sbAuIxQKU zl+Q1J7SZ^EM(&`IMQqI5L1yF2NkV1!hD|t{4Wof8c{hJ-RcUmQovk0`Q&KRO2k)MX zwORw(TzD(zbxt{DPLzaDs8S`FLx&Dht$Nx?vbR*97z^PfAu)-4d-sr@o*v+je_HL~ z?u-}6asyL(5{i^fPv78+QW8JO{3l};xR7_d&0gjc>q>JfQH1dFE*CCP%;R=c*~He7 zYhRtkwWo83Sz0o3`pl5=XHNb1%VEfauQ4P(HRBSOxI|P$Q|K`KZaNG%cs(vYl9=6) z+Sc7Ps27_+ed^xr?6l`Hs8a?B3M91YN!z5SS-Z_i_oUF*dbZaAFj!nEdZh#vrhk86s=w1o#@nadL~5R?;LwKlehbrD60ekizMXaLNlI$fh##xf#=gCKkWx~! zR;I4}#HX6rtTqlG_DeD9PJ8SLsE9j;g%G0rgK$UajDP+KAL03*yYCf6!2@RrrF^`U z{q+Jl^79Xo5F(XUT;dX!xWpwcaXCH2OdCwes85~Z4J==MfKutCVDn1W=GP>vmJ;@w zN1Qtr@W^dbdH;`c9bV?I=f{;>*jrFYVg6QrnENH$8?+_2nn0wsqEq9Ye79%=1x_c2 z*Zjb*MU83MAU3W+mBB8Gwe92$RtUdONG!tZOKW7PLb{9<;W=s*2={->AP+gRV>utr zTfw%2ML0+xy-^p&jP6H9N-5``yOfuvPGF&uN{3PRb9HWzC#Lsi?1Y27KKpJa6hM`X z77Vyy6dkJwC@6zAXn5XrjM{!5FTd5C2e0nO`0MhS`R+qp|Aqs_mP-4PcX4(1YN)$n zlvYNoIJUCk&or7{SiP*B5?ghpebv>dEgE1Ec6wYikd?38%XOdFNy%!@`IE-dy}E!C zn_0iTE-kNe<& z5)u>1Tl+i5jxMdx=hvxMpUs;#Xfxz(Hqz44shQ!Lbf;(jYnRlmSDy{**3$Ki9+Z|k z%797Ljc+Q#{Z~=EQl6GCWO4ati135RJ!8T1y_6E+1~OdvPRlptyx-SQKRYMhh;fNa zT;dX!xSYmfrVS?2dNXF+E?#^0ZpP1WV5^o**OB8GSXV(1`dXQk9^>G?{S>=QrL8OfO?UO;o{g?8dQEqB5Lvd&$S^KkBJL>ij}Y%M z;6E;cjR7eo`G*d&K5s3hrKOzFvv&aS5tq2cB`$G^OI#|qoNjt=+%Bp(+iClt927)A z#t=6_w=8pr4+}#J)4~&U&p+s<=f9-D6{A7}G7^`#gfo(zC&}Hyc^bQUM6+1-=+&1k zn>X>(vLzH16~!ARE>VjRf~2G*vU3{IxM{OEU=f$N#3e3qiA!AKQi<%MSldpD`&i2Z zM)hh$2&MKafwS@;u4p* z#3e4Lt==1#3bHToW>vVsi4d+K!GH^E@FHc{f4jr*m9Z^A@WdHcyRr?qNy1WkJC>CD zy9fTicga$BI>}IIj1u-YWzpm!lqGdMUuvpXS^b2>p{krvOTCQ6;Lq1`CRxQ|*PQ3p zC2HjM6x!+`!jvs^_FHIi`^*E!9mS^hg$V2J5Y3qIy9Cvk1x*{h^F1Nzn?U3ZVB%_5t6GUQc~&&5+BFBb_`dBD;=-YycP=)U0aH% z+f^ETme=!ykv-YSniE*((o(1Kdi&iYgur4`Vm3C+X>;R!T^77f2&^^>MaPPxcn4!T zhnBrWL{*+Nsvt(CV+x()-IRCKy$6{yCXKE!(dqBz%?A<10|n+Cu*Z@sTIqSHB9I*c zzU~}ljEXY%58(?~2#u*uMHtW`RKimq7ZHJ9+R}0m!{3+0o0uWo$pwUad`iE%NL(*z zq_>1_15qG6hP8-Pvec*~30(`DMF@&yiR+!ct1t9F-zo$aq3GNQm7o(ixKb&?E{Y;3 zWE5G+)Z5jH5I9t)Z<}f83lW0_LPWR_s-1+X^@uz+q@x7Int-rqf1WV9is>37;gtWt z*{WU16q+`0-HL>W{5B%unqyp178=`L`)~ENlT)e)WPn?TKxe-RbLthT4Ik%Lk`dP&B8(AZz=bgUo>ARr2(-{R zhr*0isY)ywo~pGC6-7Z2#@jkNz)9~t8pl_#R(DFQ2?DD{AXOP<{q)sC%g!gFo|U0< zs0-btnPm(co7<6e6!U-!W8*0r{X&@joEViM0!RGG1r}t5v{OZb7C?|V6s~<#fwBW< ze};vxJBWyACkZ0Bhm->pURgx&-q1ZRBZ0t$0M|9o-QMvrM7aX*A1^~Oaqxs!-W0U4 zEwq%Sf5Y;0kH4`d3@p|*i`C0ZNNQe)gci1ip?CFFXv{H=1r`A%Qlbc9>R7-`qd0;B zsFx=3>v+mHlAoV8WD{mD54u9;Y3x{vXe~z zcaPQx77<2YtlmkJ(ISK27F!lb6xR(KpXPunhTp;K3)P`ww_A|`poI}2TEAf`0aXai zZ4vFd<$I4bI8tTQK(o<3Ihyx&swx%{?UTyIY5^g`AAD(LwwM7B#zrJUl_-E!QBb8C zRhyaj4rVeD!e20#K@klgQbESLi0SQy_e*ox7G{8F+HOAiY>Vgsrjv+MLc(I9`=O~+aiQ5?*Ck?p3TeyK?DL766yDE5ar+neoPC>*&0HJQKxq>XkuIVDW*mZ zY_0w{p3ZTop^a~$Qz@}x(O6cX34Joz7Wx)_SF~9vWdy_DZ1qcL{iVP05U6aM$F|VU z1d=7CD0Z7>;385xMt@4~x5miIz_!qCTp2wL7Y1Bzv>B{MP7WitlhXrO7&K4<`b}v4 zZERTs0!7<+5=O^&v|x)twuOkEWfZ&9un_B&@CC@MLf}+HSiQ~+7m;A)NFQZM=ZUCc z5Ga~HH|f5Uz_w8R6yIo}HH-T+&nzUNV%Qe?L{+8hv{FhE>=ueos3z3UEVhL-{ANM- zZ&TP7!eq9ES?gr%*cKut03gGkaS?)L zA%fYv5vGzi+d_CHx^sQ+#l0v4DrDdeClROHHwWZfU#g(XlW1C;lOIM zYNq&yQ~b@LnF~F3x9MXt++2?gT9|K0N2*WchnwquqPkD?JR6YFdWCDpShiPeVq3(% zUQvxPAu6bTF=AV|^SB3h5fKKSp|742Kp^Nk8BKvZa3GOES)Mgn-vERqKnU=zw3pLkrkB~WV*%X-Hgxe zXxl?nir<*$BBB-eijBo>KG^V2i)|tN=BMz}JHoRrg1d!?AOMK}`-@PZMNnhiFOEeS zRN)0&cw1^NcMw?~?Lzpufk2}&}1ar4YOwbX&tZu0>y z%rW?lDuPloaG~E2GubGXW&js4jBO!}WO52Vr(G@~(kH-@*$PkOabz5<()L>m`cH~u zA3GwtF@UBqaPneXL^k$RRmEnpfT~7i$iRh&q8_nlTNsJQq{ooc@+^ny z*~?7;TL7yiQ1Pv(V2%~(hT(|;B2N>D;1+A?Nu`QxwY{iOgAxw2WhykNS>u*ka zY>Sioo2bhW(dpRczKanSUnK-Dg4oR>AV{YR3ka6xRQTeiKwfxP(Nqkr7g2wSelZcu+;_yHu9g7O^x!W6ieEpGibS|12G2wIUfL zQI!$TBvNs}h1XsZEVZdO!vX>s8sXmC8rcl;XIuCJ7p{fW4N6Gwf#9AvLi#N0o}iqO zL@}|0BgeLonqymNdv<6A(1OAVXR((-5W+gf0^34~!9t~#ZGix_v=ocI9Jnx-Z4qr>n-46R2384kHgaJC zr8jir8P6mlRp}RW)2z+YZ*B>F^@?3a3`JXCn373E#(Zv-Tb?quK|f<*sJU;Js_NR? zEEZJN8I@tA*I<*PzP+d>+whcM+`Dz5JpU2Be%cxCyB-tbq1@Yzqb8hI7XqKI}r zNvAApH{o8n>^2J}PKQbLGd~z&bOwENE@)bGTr-UKSb`kEbT>_lQ3V^j=IN_349FQ^ zXfhxj{%L(bz zzp=?EVeAxK=zOWjV^Yt8s+Voy`r~3;*unxA#u!l|%04jOF6O3j4f0zR&4}traUam2 z*n|tZ>4_;2^LLWy@-vMc+d^oMZDC5i^!_%w3_j7YEzD57437!v#BNn6DOIC-N4@PN z12%x=jwCrCpm{#qpl7-r$MT{CWq|t6dCXrCVqJ&r1DFG$uV_SH{rdTx_g+`6_aBK_h z?IWV9pFlxM)rXy;k8Pp1|6;|q@RgItwkVH%;LW%&mu(?+w3nZeX=?gYBd6jK6faZK z^yy*s=u`v^uq~XbZ`9{l)XP{Vk&1{Rp=dMKE0}E&wYxx+Y>SG>XCKN%#8KiTG07TY zjWoix&^9NesfKH`Y>U`u5;3m~?Q4!Jn!GR#Y_SL>%65w0MZ|1yBbcaud!uwntO<%2 zYo+4Amsph8+`cu8edxfzrD(P>`eO3(F7L^VRIQ7^TBE+IDLflt|4Fquqj09K& z6RY;+IMAplXI;3qdl|$4mH~=Wh+!(to;*erYHdooQAN;#La|yJ!tEo3k`hKyw)we+ z1U^6dOd{sQjwpBOFajo61e{Qvew1}Ru`OcY=Wb|B=w$%KkZs|u43(5(4Fg;lXImIV z`%O)MYL;Q^r#&i>H=bD1^P^!~1e|>OF)U765J^r}aLbBhTNwFm>llJSv1VKNzoyGw z#FYxZELdFBYzs3|gvNkv5oS*lI!cZe+oCcD=tl-Gd{UcgN+J||7mH-nMebZZbX+&v!jKDuM$kYAN~M#~reWZw zFGCn`iV#7St8n!*g>3;9&bCm51&1s#HC{o5x*&+y7}8N2LS9Irv$UiPxTq+jt)kf$ z(VQF#gPn8I*cKYcuv4n8wh>0>l!$Ju#EflWBp|b~*cQCq)FfWsc zz{9WzKGyZsOCOjLtCX+N8u*p7UMC}yh>AjyJFR7SexS}qs#gWdW$cUM5`eygc1-l z#k;*?726`xLS{6!h5v#dsU4)r^fljS296C!Nh!7vjEf47Z4m>5Kc*nTNi%5mOX#4R zv2Q110XZw8UK$ks5OevYr)7;HI2K#QGD!Qm+qk5}L2|NHvteWg+rkv^Lq?YY$rP|E z+OZ*t!c8P}`$}mp8>QSesJJp?U3SISBQ_`Q z@z@p>2L}98*%m^NTXGcabF0w~@l`V-#bOA(fyJsDLK$Y^+tqDY``sil=7Z)By_BjY zrky$~WyZYO?WX96ZDAz+sXn%aBEccmn2ZH;$~0#|Gn;LpC;e%FL0b~sRm7OGsdtR% zQU1-3ZJ}$-niAVW?+L%YI5olQqa{m-sqGZ$opq+0uCRKU2p&WmJHoI5>lJjja+$cHyZluKhEP^2TDib~mOT5JpH8H(DSk;921h`={JIR;5A z*cPDe?KDK|R&2ol0000&vXZ=IbppQ^j z1!a4A9VY^7TPs5oa{~f<7i$9o17{P%4|ZmOz_HwxJbU}W27bsFz)uTO}zh#UDjWZmFl-1wGH z^{ICp7Z(D)*q-AT3QK9QA>|~SZO~8?QR)5WVw2}HKfd@JwYvV;=dceSpdn2_vr?9l z_PAOsc$_BsU47Uq_AoX$K%fAMmHzP~@FBO>?< zVsFMnq%0*%AZTT4K)_7POiNEAX(OTIW(J^pvaL~~+(lIj9fFNk>TrBN%oM|lWh~H8C1IG^oJ3U(yYkLzbOM-Wt zI=WU4_B=#HpnigXB7c{_|6sSY`zI4X?4Wz^p<|$>r~6NEdlSR|AMp2{e}lgd=9D#Y zHZWKIVPaumX$P7D4-o@B8^d30|L#`#k8XBG=D)q~s5r$ebc_s?O!V!I|6|<0dqfRP zjEq5R`9H?U*qRuCxbVJS&>$kZcV5!{lbImd(EX2a-Z}Jt|Mg!Y|0^EK@Fu%X!T<{VKHCY1DL{f=?vVPy zA3hL#5c|Qe;Jk8{;pA@cYrZPWW1^XcZCF2cSl_jhUJyUDM;0|f4v9aMVhQnwX7}PJ zI!B}{<24)6|3V1$sQ# zKBzfXd*nA~hXHdQV)U;YE!f&;H5|V zHO!Xb&J)fZLnp^O@{6qNl(dYrKcg6_#QM1A)HXLd(-boyOG)p~xFk?TX%^pD$!N^U z<;EUnixi}Du6C#?xWG?7%zZH)YgowJs4MyEVy`Z0@3BnlNM5@{^WfP~FV(K<%X8lQhV@sd|`la$=5oE~a3IrZu(`m_DIlN#2ItG|c(`UQYsH)Wy zQ-QIY*s|;EB6r?zua(37&pE+XhHhaoVJRl;ZNILMSabDZym^0IQ}khrXn3CsJw;oj z$(?vGh(8H4>=lM>wHJsC#gUY&-~PD=n6I`iZJ@hi4DJrOX%*>UkL`HaG?jzHXXuJ* zVVw3OU50IU!6q9k8C^Ko6ipxp+;@oPRGcz%dS){~tBCLv5tV48QW#1``o>E{RoJ46 z95hppz%rJ3R&)-S5lnl#7f8w#?yLk4hf^BP`)$|JH$Uc!Rg`0 zu%s8!t3EX! zSiA+R!8%AssE^UmVG*BC7cUQ$TJMh-!FYel7g>A$mUWJwvcbhoJA@gpgup#P>85nI zZC=?kLWAG<^2yuH7r@!lhG3CILVLXfPZ3xoTY#5b2llD%_*1+u&21Edg5#atx zA&M+P=cva$RByi&#jxei3}+DF7VIlkTaFrr(e@mSNQR)7{GMUxoKQ2lSM$D8)I_t) z5ze4JFU-@+9;0eFwaF!JoDaIf3Wqj3DsU}TJ8#zsg<%{e1cgm{;7Ij3GE+x|J-{tb z7yDSS%!A~}_-) zIlJa(*-vab=qv3@t1^tXqXi-52NbrgqoL1Y)F%%|_Z=9=hcXNsQ)s&pzxYp7tuJ9R z1UYW*bf;8?a}fu!%}2^%$|Chu?*)e25W$<(NtxXPp{_b8RB?sA4YkhRYqg_HJnQC# zN9Ii99fS_+a2)hM>z0kT`t7UXWT$@=af@TFh>R8XZ!2%h4qh0;UA_k8~CF-tL4>CyHGIc1R{ z%l4pi2-iXlQ9jY=GyWcDFP5SDA+y6b$zL$p-uk?Bx8uW)*rTzdxm8F|*Gbj1zX1WX zTh27rxG=n4;lOfY;%yHbid5*nXMuRf;u8vG_wcVDacwuydnxRRwYU7!!<{~dn`dG4 z`}N5;Rqth&|N43bYcM2gs)mtsqIho+zm}%0O+Ka4P${jxcf6+YCk$JU2yDOZ1I&@m zd5hlAvu>T_$AG&IjOF%I&agxZ1-BC2IhtJTK&w?DLRj18IlVbc7_qd2A!jWVrgFi} zX94_F7vrgl30IgR>mk$NpeS9M4fK8ySZm^`xd7xAOy+5 zmKL1FknA^y>}&zlqOk~c&VW0A>TtA>{>7E?bGcs%mtp4kiOnDaZ1+My%g1Ick3i5j z%Sq&nd6eW3*tzIUg92@oR-EI`$u-39SfnKs1O7USD?x+Fvd))b;Um9LF2e+xX3)Ev zoFV%jWKPFa z=4WA&@#UW>Z7?Uw zoe@S7iQeMI_JtxJoI@z@x@Stv0X#vwO_T|7T|Z#Kqp*RDNt7haW7=y}O#MpE1P#sf z_r9P3BZ0%p$FwMb5O1OQ9-11F-DOyMk_skeb^wFsUZHjoAuX+cjZ!l6KqkmJ@!i}o zw(KPvXMb`!*qnTtgrOAlG0>x?3bdh^wa{@XcG|#lnQ-{au)M|;=yUr@3x~I+ z#8=p_Jn#sne@ee6#C#;qrm3x{U4SYG0A{m0__jid@m~Ji4x$eyc}YyuWyR5i$8^<# zb!!ggdYZ}D`3)T(HKK4lyNp3^56?I;I20S^8>Ay=_AGV+u$X9ie){`|<%4 zO53QH?pbh;RHMxlUX;XO!aAtHm-snJ-`0XKq38_xlCTPeV(ury4G-qg>i9U{(xBet z=%_)7%iQFbcDg{`c9Pj@^`j;9M~h`q_Ew4g)?lhkfDmw1J~X=vZMI83c~L%qd!L4Q zS16X|w|E;}pDBSZvT&ZxplB9_FJTvCa$imhyCymhBP)D%cShqx;d3|w7Ck?ghZg{Y zF8*Y~G#W|JX%+9OG8!C8U8E0iPRP;27#)_{;>uy%48RC&CikzUdHsFrlnFCvz{5r3{(Q$r{h$ z^7j{i3;hN6r{jo8GmnFjX*w+D(#WeI5?~s z`&_W9lVC8oHpIblG9_rzMG`hfk!Tyt7`eitn?`RF*+>Lmcx;K(k4j|Vp53|~!#+$E zgb+ab5W_F6H+EX<}>am*Y7wV%?*r80rEIJSL5teS_pcpHt4I2C8%N zf1}d-&`VD*<(H^U>SS-)abVJAR-y?Oc;eXd;rr>v1Nt$11_QWPX19)Ghz*T>6d>TB ze*(V8AfF9BNUl1DK2yy~KI1y9 zyz*Br3ISvmnd$2i(Z2o*7SSIz-fhsvTBGqYUM9a~S2!6X&_=YduzrA{tSMVy`kCp< zKjy*VHj{hi2GFJVv%DKTG?NcJi<@zZcG{@#h=Ys;yY(Mp7GO0_Pxd?a-LC#KR9)~c z_w1)1v#H8s-~<@d*>%jpo0nh&IFVD+(_%(O=pXQ+=CS9lrzGjRcLk1}$Hpvoe`Lkv zOvW=mN1KbWs7xGMZL?xfQoZ?I;%QHgYRR^R-XO(%u+w6r7kGSy_@@s6 z!A_S|ZnfWrWrV(p8%=ZcX#ERTSYL26CtkH$++He)jO8HNR|o5s8zxw=%1K{Q|0=MN z>#w){8Uky|@fAfd1wmgsLl}NbQUqk91R8A99c4zSpA>`y{kV5E8Bh54CZQVCJ& zW)Lq9lx7z1yO`)a>OpX;y6cul!l&632vR- zPCA{P@YIc5I{mwd2zkZnz2&56;zor=YmlVT!5F4Wev-^1I3~S7@fW$OA7Us7)ZqFF65|4G6_y8 zPaZ`3AkBU*7&|OdK`w@Mm2(XCYhAcGseq3+ljZb48k{IA=&{mvs<+iXRAgOn+_}PB zlEaUSQtt^5{raMbfQPJ`tq5U<%mS>h#?9K+med+>ff>zqL6)g3Bh8_qG^Ei5)P*)6OOy-tV+^ z<3hEI+RZb`|J;Tah@HQyjl0Tfk;`lN`>g4#Q0X(&6HoF8>5OOeO2+DvJo>;XldQ2W zl3z_q7>A`R3#zA3S!uw^x!RIC`oJzrpEn^$90d{R@ZH*CE7~c=kl+$WCnr7hbJ54+ zsVq##uUt+SCr%ax$MbB=vYLv4o0kv-IPkLz8xwseCxoMHQ_{bYKiR@x*ARFhhMsAq z+=e-2w>Cx+bfCX${ZcRUmBaZ~|Dl$Vpa+TpMYu5&ww6c0VPhRU=Z?fGJgHsOzLZ2E zQ}tMwtuA{3epI9mmn`R-v$Q66K*?8_b7SuOgfVqnZt@9eM39%P$hu-l2#0z4vxQiX zx}i?L;i6zwTdY{ZNA*Hk8LteF^{OIOXA}|1Kfhb= zYfa~AIi;#1Q0^jpBMTBc{le}auCU>!t&@|lHI z5AYm5jTX2;4Khpj<=X?R)9b7m6FjBZ&Go=bliP3E6BV(4n5$5MoKLvLZa_g>8}s4w z=!m$D2G7}XLQ=x))YNx&95RsIp0XGdP11yB5~!phoM!>^x^%ln(Al2?^AtOFAe__% z8BHC@oT&ysrvYxXxd-FTc66r|do=2^ovko6NI3O6WftmmjmYBMxpJ-H($*I1gpk2P z2JWBA^u)p0y=Q8#(3OkAP-EPIvynwPy7$y2PUZO>fM#sN!;wSf#T}!Ckix3E5fLdV zj#JguZb@glY?c*t0^?5;{Hz^Kp&Cf^@gbX5x@>c^X9{u}^8J0W7UmHO4Ang!)`c}Z zFwV0EUQ9m1;u=nx(N(wb3NN?CDf4zx=l?hZnVAd%!qM4QQc@-^;gXhmva(;WBZj#B zbz=ENqF`ohfr z9%(e&w=3f^mTUgQE}fz#y`P?a3ab3AB0Fasr-${XWw z_dK2WZR1@-^-#(|-huA$*@G9KlW=L)I{y&xTvU-M>fym7ZcojHkYp~{0oo&ejjdh^?UdcjtU!_`;Z?L^mE0=DP*ezvYDR7{a$*Dti?9vFx`8a_4iRaoiE z)l*x0q4yo?MAwmNUWFZ5e*Q0No>zqe+@-O37BAm4hz|N=foe3|$klED2;y zG!Ao)CJGK52EuKU&sZ6GYCpc-*by;MMsmj){CrMCuWm1O*7cwQ62fBmq07);s9jGX%N`e0HCPp}=QcYhAyM<6p4B|G6r7)BKKN#5$!(~tX{Vh0QsnymjC z?Oxtja2YLzG{yqkEu#N8Q0@GEhWH}sY6XHsiZ+FBU?M+V1(yB{Kzg4D;m06SGsi&Hr2HRgM zoj1Y`*QM*bXoOyZFO*-prpYSrA~+|vSrf*kElwY~{wc~nd4^W|Ii$tKI^j>uZYWKP z*P4)#c0XmF#Bds%qL3<<^nIPJrD^?(0%f*GhNGzy>r9GuRAfD3JuIvW`THg}=@laR z+Fc_ai$y+z1TV4!{wagA-F%3--I`qhq-ZQG^WOZRPh@iGu1f^i6wMtwO$2)NJT~Qo~x&^Uv5|3{rw7Op>@sLj6qh62$C3f~$znpDX1f<>hObUL%J+Etx!@SJs3K!#2 zuBY=?1W#NPsIQ+oku>ye3_WJays#jSE0*_OmzGE(^Khq2N|qZ=Cnn#$TpB%Cw=kaV z$z5G;HDPgUh_Bhoi&IgS8^}BPu<95vb@$AUn9)@inlk1DSB1S~N8oGn?JN?0MFRsw z>^3!{P3{o!j|x~Ky2l-JI~vVt<^f$PvZ;kiymJH&HH#pU-rI@;J4~bjun8-YvmoM7 zbJ_2_R+jUZhs06~U%(3uvXvNA;-_`h(ZmdhU<|nuw25GBI`Fz&dBiZzc*!);fKTbt zD}ukz99Pzc6|gkJr#?1Z9Cs~fJA*w$V64#7>t~Ss!-H8^oPYLf#>8G(U2Sd0zO5=) zTv-sm>pR&fCz?ktHL){)>GYG@i6tV0D;>H;RyraG9&!&H=xs(->zC~ueNw_QzdZM@zb=bF(ihFU?!Zozv~ z2rjoGbLgYAbdl1FNiNe19RxWD`^f;h>i8t|cYMe`Ypt19aB-~!J*-rdBOQb}Sd<6evVl(n`UdDK@?8!t}K*sT>2`AtppFd4G2F$!XV*+aS zlccIG;~{M*(kz+gsKMpPTTR1w7rC48dss;ws57;^oG@!bTv2*_c6QxDq;J zXTf?bRZ@Hma1tZs{xCp!NM~tA_Q|w-$(87eImhA zbD>A^b8CqQ9%cykKT1*0jdbv+Ez4G3xuS{-o>uVSq*q6a=lZR4rFGZHdhgd4YqiEI ztr#-qQU;5NgsASOFmHr8I%G;je;Q({3u0@Sw_fS7QJB!^ntQGMX;x5CIhmyjJm|J) zYw6w!R+$f{swD9a!#&T*e^xG%U`7E zvaU&MF^{;qgA*QM0gH%l0+sv2b|s>_<-ORZ@Hc`WNF>u z&vB6!bT^ylgfE%u3bsL2~x`OLz^Y zKP1xVczKY19Jczk|4`Y99CVhjnP(fb}&=$pBMk12w~!}lEhP#CcO%tV9fjHd0@?CcP0 z_MSX3@KMG2AG(cY6$+rlPRruu-y^0UZPXDJ>>eYt3Zr1y=~*A68)`@6l}FESF|x0_&h zAYm8q?EO%TTV8Hq@93E0@S>V8&*vuW<{IE?=xBwF>D&_>iQgz3YC(J@V_iRR{3vPW zc`VWp@lLZxC$DZ*4r29O1)?wOCULM2%Pz_-$#|)I0eaNv&gE}*FTg9KTE44>>+s^W z=KNb$TnB^lbSpELfoIX-kzX5v%wts;_KV?+1@ZcEob4(k>}`Q4NG?{T|EN0T;s)&3 z$F`b8a)2ZQRtYHpk`l#UoZ>ghpSbtOn28@7(hlR;mHI5-{rEqd0B5)_IRn9r@yhD{ z6y0CBzAHaJVxaK;akmZ}{6tE>WAsNh@(Vwte{{UR>zL1CQ}rrr9az$dp0*JdK>o+T z>l|`uqe$g|Z~R#yy$%{CDF4y-lr8y18udKB_+NJcENQ=@k=#@a*`A8B*-DoDYgVA? z4a*8&41|T3!PPkY&9(nQ+`mosYFEiFd(k;&?U_ihujT((%GjFasB=~jP8#KZERdIi zSY{FiLt_KI;e2XgfnV?EO%Za#F1x*BfU%ibQYLxc5izZX2BE6j>i)wP%5GtPzLdTl z##oT=E!E$t`< z&U{sftuZSP>DTq|+162lMjj;$|1H1ukm9iN*aq;w`u1!^xN4U7q)uy4lkuT;(wPoD zOJO+s>yA^?ewrmOPQDu$82&#dXw zt>)jXce20t#E%?5qU)9Wp5tz@%0WGla@t~V?>N1-CLpNC6cI6A*U*3*K~@)4vh{#^ zd2kl6|LwW!H*Xe1z=a);xH%YcTuTe#5{V|c>m8-`Px;cQ!H;%hkr?C2-Aro6-wb@((-8=giWd3YKbRF>2C z<&@t3Hvp#L>#=LcTUL+dfRz6RI1}>*(=BbNq9M3nw;k^X|nxceWDe zqUb2W=FyY-1(_F^yBJii5*A>=RVOs6;<=rW4mijkEC1FH$x?0hO2rKf;=3NZKqT${ zE^GpK#^zF=q_V_&ctTLKrJcjLv8UXGkw$A8g$pT>TU+s9!^W0#aewpPE9?0E5SOx}r!tRE;TN3NW? ze-KSO;~?Z0T~vttoaI+v9B)25o>HBiSqVICYN`m+!FYJ#^(FnN%aFFPkjWU?yBp%P znAzCiWDBD(ouCiUO|O&3HXHU9I<=TE@ASHq(FljB`gGtO3lA3aFw zi$a*-nw1bTzpFgpVMi#%8Q5N9)I3|^SCzy~?zpAh77f;1*O)CI$JWi~^S`ZDY+i|g zjVNd`R0D1X3jEkr^;FPEyFc(&vJFPg4PYax2TDoPhTv761GV@3-Ek{TDM5F93D`$C9tZE?LGJxEkTFa zC&KfJWjIeE~ zR|k@uo6q&7LAW|Qt(ffTh=?z!6SLICW^djMO7FcHsb;omOo!pr1JQIoUYo)MTUUBXtN3P_G$q>E_cHL4&VK`dJNAB z$#QKZck`7q>VnQZ>-5~sqTYP7nu?dU!E!I)JQRQZNpV8;8m7TQa@}z{R(r(ek00C4 zrX>4~>jiIP=!y4uH*V>}u7=Z978t9dwBB-o-kW2?Csph^d{iNk)TMR@7`O7K*yS0d zy@#+0aUwN@xQQm8)yk}XuMTTugn3T;z(Kp|w`ambwzqJ)&DuyX$C$meLHFbm?sAUs z5d5~hl(rW^_nn-$6u$1D^_DF3%V+RT=iXtr0~t*bD!LSeUm_qsHjLllBO#sKomay6 zLMdq{@D@5S7VjX%Nw!K8*}N6!zO|;8qLIzfWQQ8H>4fjKiq?G!*iYEB{xOdst_Wtc z#wgvknrye`$cy1jtxu=IPcD-%Ep@Z0kfb8!iZ(_}k`QA)X~ zab;(A)=8L{&|7pxT7O2<@+4-u;g}!O zpno?}LCp8Xd5cMVyuFsr1g>YSHRAi$ZuXJFC}dp`nb24?@VwAmxN?LGBb&nSsWVo# zCXi=Y?2OGy8vs4bmVMdqFzma<(iLpH3$s>3BIwLgER;J_j#TVY_MM7mw-(0q_)~=D zb6kpySP_5Xq2X~L)bDo_IjTsRFM2(mXUN#n>&r4;(x~47Wv+cJ7Hu!d(rB#ANm^bY z(LTS4=7KQ{PR58x61D&l%p+U^%QjV60{Y1_y7E>wZ+4#@r*#sxE^4Ru4vLYP(EcRu zBO3z&GR`fUcXl$24#YBiw^kc&r_Jdu=H9=kc2DvbP4&jJF1KYMq)KE|yCHbpm=@6a zdIjq_Pw!Q+zZq;iTkeHjGP-PcXWay?BaPYcx2$-4xwHdY9dghB*6JHzKdf%>;rfj$Gev;o^(!_-HSHV=*UubqCzi-7;>~3Qe&VjgmEtQEyjj3OZ zI>Mqn&tX3Ec`?12U%`hp2LDwty?$4IMK#{CGx-U=`vAb6%+*vD-~4l9?O~Mrc7e!p zB3XCxAqFjbW31BU%O?QOdLX#sCGCD+meyDr+FEbo`Qz}o+w~py_0pZkotcZT8m|jq zrAy>NhLJ+eu}|eqsS%MeL0yNR%OTES53`A|+I{WY_c}Gj$3EMr>htBpWk6&tyG2Nl z6gJCrQ8$zkb*}l~Zd=o)_{!0-euLqSYlAhC$OMzKPMqOdj?kPx=X6-4cF8k%TJnUP zNPN|))@hh&8|1mls8@r(4G#LND=yQ9JJ%s~YUs+6cdZS}=@~-J*%85H`CQ)|Y-Doj z^22Gly>n2-=8DwSEtgYg_X>?vlQ=&wv|v9^3h^U)Sk27cM zYAilo6~5+!*rt~2l9r5DfwJuRk!(1OX6?#H8m;GZ zVH~E8Ka16hCz4hx?X;%Xl7>%Ul5N1l+wwa&$x%|~i8!ZWT{F*KC4A*53&&PgE?nkh z&7zbE=F$*!R<>NINab8t__jV|wTa!CTA3Xnn`YiRXJ)N5Y}Gc~=gmI#REmnId3(#4~x<@zKB6V}?-@wU1SJfq|%UA}$M)w)a11c*++hsg$) z*#-?o=K0y*qZD*B6Kd;{2MDEREKaXGES~o%wM8!Hh3ap%5GzXRY>(=Pz3wL|uy2`% z54$G@?t5mNdfiMiS4&!1TM0bppGQZp*>Lhx)$HP3q#evl3Oic89vk&Fm3U0ZYTe(d ze@t?AM~M?wth6|&F|aGvLB;(tMt2IVy$87O0@7Tc5x(nhJg(W=c)n(D67erykEHsk zTiO=jg^o4j4gI@#%wowHz*})!!+u-sU;zAyw!mcm7E#7FK-9= zyNPyXn*AD;_IdVv(&S9N#@qD?t<5{>yD5fG>^jpQ72gg>SE5>?nGzMQ{q10xur+s; z91Qt2cgTO4n@;S%@jg4Id%gH(@m6IG9DSZ>zK=_vqZ*Y~Jhm|$(S zP#D0T;2*C@{2F-9J#FcoIdo0P-LxPGlrkFSPvy#KC#TP|YNa08YhkAdC%UVZJcycz z?v|4{+&YEs?qRF0J2TOQbfbsOHNGBq;FbK9seUNdTfV=H?-Ap0BeQqb*q(rWq1@e| z<>j{u?C4}0kj_3-w0)PNefkEFMPCCVShF~j;gE!6U)vsijDCC%VO8>G^k;*m2rie? z*%|q2g*Lf8HXF>jv`H+t?Z48^ML)WUe zKbCQzGkCLF{i0bHD9S8el}A#6a?sdEh8Bi|{Tn0ou5}5|1A7pM&2RHX2O)7&hS?P1 zn|i*kKQSUT4dL26bU}#4>gQG&)}ea$?Zvmz8;!;5$i~bc{WYZaymr`Ty2YEeX>iXo zL$Fv%?IRTu(z|0fqwFmXEN|wgIal@T?ho;`F0EWq1L*kLl{%=uc_2oWz6BT^;20K3 zy*xO*>Clk=(6czRT712*Tj1?w!hP#in@|>bg!;MsIwO9Pkka58Ffgc?=~|GoKT5YX z*6Eos2yef0Hy~pT**{>j@s{4Z7n$&4V^LP=W-LX5)jvIjnbrd(L3Om!5g^>FyTuW1 zvO2mvTCt?2Qe*$M)1?l6^sslHHgZ?@F?9N{B_TJXH4eN+YSFQHz=!2i5L#Yb`2yBv zti(g#wIcA({N=R6BYM-$ik*tvXq-K`L>O~{T^2$@j6efNWJpB?A|g@Cy%u-XJqOTj z8~t%1)Giq?sZ~=Csn_WwN%Q!9fwA!#2Ov1_u(nHtoQ&9Pzh@BO-pZCrDBi@8>H4 z0Cs8PU-Z`O55}zs6Sap!8Ww9hEm5PmJCdy00A7!z*30c{Sad`tYO`Qvoe+28nvT{0qG_J_gT4}Hy2Oc$;Tvn+_kO{J#L)zTCVHx3pb zCmR6P`vD1OZVwVTvShfGog)>an}Y!*iF~gGXhARaQn|WhFzEG2=boiy8c7GZX2xZk zBIT}BR-F&9safm&{BGPV-9U}8+{UY@!jq;~5E-1c(alTKp8m83PseUIgXD8BAgY&& zI}JYYfGruYgxpqlPGI>68|gK=%HgJ&eCT=0r3o_#CteX4iz%VPP4|6HsaNh$bEuW< zVrx6goGA7Ol&BIuQ$r#jkKE)U=w=wN0e8=AT51S5*;ctnA`Nc;v7KOp2`KPmfmb}o z2SPDqn5W&jS9F{xtl+Omy$cg@xnZL{$Sv#~angj`zJu*_4@yO748)F2H1gO@ zO&|DC^a8n2tx)YG1A{dAIWprH^f}FsvX@=#yi~Qu1?g=)jv?+vPD6#oC2x*J%~9g0 zMcdN=Hfx6meqg&A!|7O<%jIo5x8o+b0H*UW%Fl+!d$%#*tEopT42C58BTu^576&WK zw*tD(Xc(jUn@WfUBL%I0mfIrG710J@cqGB02EW*!oTEpA+qdBqO!tgN0W;$G+{y4g zR&U&o=_1ZN_nzHrZ`?PW7EsshZ9cT5E?vD?DYuqHHgJy`&~$iLuG#W;PM)84?herL zPWTLZb}e#kj`v$V61)LA2!zZ023FwO6gHjaG<26yGs;{Z7GG|1(XE#ctp-jwyGlFP zCkD`0uH$QZfnI#WC#Xs_NA;c8l72?ftCG4L1o4W7;1S$6m3QjARycGW@?n1XR->lC z)_tOb6q%csk5pERID=ozrXSs}N)O51v3Bz>J^k@JuTc0}oVb-bPW{|%grEX(3wE_> z(oO_lPuee!y{@0Q_D}IL zJvx>j@n}6ygPM=!@ug3BGvD?(8_H?jvn1FwJsboZHA-x%N)!=H(bp}`h7H{oqJ@>< zj()JP)|+d&6(^cpwuIZ={&YVrb~UGN8=w4h)0sKaNY@_RKe-Mn%IhRqwLH?RxGb>M z9WaxLn;qDm1Xvke?{Ie}NN5YF~N7_@(D#TYuogM#E)+0Th;Je?*jZw_ylk7|SH`o`+( zublv=rp_OqDN*1T9YTclKouG{drth<4WpEoph=s<(}}9Kb6Uf8P!=U?4vzmgouvG< zclfvxciuF!2Rb(v0F%jOaXiaFs4Ax3t0hdNQ5=z*A5j=0V8iqu_dKJKFxlPjEwQKD z0Y`bO!uEh)mOb}KO07-C>7Wzo&R7fLe^Pum+a%GJtHJ#4r;ruW%@!o3#GJ8iTJPcV zzuI18O<)5ZETg6|gXsS}1Wh(Qk3mQum(;o_qbiCIh=A1e?;()w=B@fVPhko#c`*tk z>#VUEfB!iE|_i*J^L7Ld;O364n#}jL$siXvI zPBPP12P5_pa*qlH&w#QnuI)9DUNe~g{RHH1BSf!TFyCmx>t#-1w7~N8`YqEheu7mQ zcce?n(ES-z3OPd9q^Q#9X5fxm-Ce)3wSA9^`w_aoMl%mcnSVu;u`Do#^{>@J4s%p> zqQ&c;%$X=BLd-IModf*{phc}Q(riQ(L2*l=$LUG}RkhU^E^%My{)_`u5z1%*dro0? z3Ekrge}opQ(@KVpb=Fco9>>!y_@4>7)h*zdTNad;qjI5fk&KwDpzZGtFm%*VRyLkF zK-@EV3{XqX_SA4ICt^m?EX5TpP~<j}?v|g{tbpjY)_e;q5vEYz|Q}`ml81mbT;8 zx6qm+DX~sYlUY5;Vnmhw2}QjSg{8cL{d+-$9uWO?dk4p@mLOn{=SbZ#rk0rsTE2ln zd_p1;#Wx*x>Eo{{2%Tg3aKc*HqHa;{ZftBUJJsA{8I4tJ-Z`f&HvJ^sPMwFBK7mmJ z-}u2IkVCS_4_0q7_z<->e1*)H>oWbur4$sUhK`q4ZWQs)KXGo*p-{lB&>y*MR@O@| ztxOdflZJi=6xpiIHs0|&C8+>T?P4$gfOKotm48Nxzyx&N@gJvBLr7d`x%=8oYOGee z(=if7Wn}`m;4v+Y<||!SqteoD84IbhMPVLpfT=0j$@FFzYn;~Ka#KPF#B_Ehe$$KK zjFSXkGp=6}q0P`6R!+=iF2wNS1R_p1YFM8K>r(_xf3>O-);cA~Q1IXGZufNuAUh*v7zHTTtW{Dw_ z{{alXaXz8nIfk#dI@LH2%Nveox+zDjN}^VO|LyN`gI?osBaDT8Qj|)MG*~Tk5ESzd zy&U3Z<6Pmw3X_xndR7^o_s3s!taOkoSN1fh&mF>56*d91wW5vjY&h+scFNqe_5ky< z!B$4Hfj*9{JP_fZ`b4YOPlcfW|eyBsyrRYp9=ISr|xPuDwoe^L&cSqSqUM{m|jCYVq2^b(n7F z>gDxPJ?QGiXL>Bp>(LAMq3wEvN-~3!<pqM;#F`ZmX{<8~3!F`D+w@dQ9pY|oZk0Kk^Yio1 z&v7!Zr4$2UAer^;^p|Z`eH^W+qK1hY^jkd5Z);2cDb3MrTto8>%652PX{RZ*=v;dh zo?)=thTwp`&z0BC0pJ8mjK0=t=tlP5yggyBD)BiDQM+?-aboaw*pBh}lgnfbG?b?m zXR<>LBBkI<|I_Z>tQ{F!t?T(?9Np1>_k0b9vhPHvn`?;a2%o`{0Y4~>?-|eCHB}-v z9>iL#-#w{18LqVA63|ll+74!KHI0mL8*b%w6e&`?i}&TKjbw9sOo@U~3}>mVPb_b# z6-i?;-w}YO3b>4;${;b4!t25Le1EQYlUP2M!h;<(IPz_h zR3a*;yq@%skxs24+zYsSsj}G!XhWqIjk!#uQF}O^ywH7px|2*{43J7=m6Ma}HUa`> z(`^PYZB)jxCjG6}7>Qoh-UHro=$)f%){StEBrZw(fDSY%c%qZ~n!9gS<(cQFj`4UJdxX?&~a zHb}%+5oE6%bD$q-=mtRt`N7<22as|tHsf4&{Oc~j^mhWpsJhDkkE*wdimQvdL=%F$ z6Wk?eaEIXT?ry=|A&|l?!QCaedjY}Sf?E~tP`Jyj@AlvKc0ce?1I8J3)?PN(oNFVw zA5r=LC3gq^dmrI8;qZ`qO_E%}3*h-F}q00rez6u*SbinO>Cn(}g{mJzIR-u+pR0uzR&IYU3aZEGy97)>8 z?JTD|nP}pkb)<*O$x*y{fw`0f3fUEzUCpRSM^&Q!Bc&3*Vk8nAMRS6%WArF zzfpV`0B?u)Z-IDQw_MUoOX+IbwtogZNRUIj_zau8NxL;!#>Hxr>9f}0h%Bc!(R~)a zMAi8MP7;gf{W!zx+41(rQsVZDJ`|){L;lk>=_U*J%~q3P3+X@E`bw4%{fY$P%qS62 zF9!MN8lC0an@{t=XLy*#RD3Q-4XN|Y#Q{XE-j*^+S%`?K4wV!MXQeZBtb73~U7vU} z3C@s93&OI}j~IUNB)tO%bK)cjla)D&Dj!mSq6vM3&?i;i635(YJ~vxA+u0RBGo!Az zBSJ4~z6*kZ0U4%+MpkzthD zx#icyk0#Ms8Pzn~gFbBj8)w}E`3Lu=b>GmAjg2K5ug%W(M&mK1bGy^oBM559wT=KE3kAmsSl(tQ2Rg`@T~fzMVJfb_eiNW8G4$)=0j1|#MXYpk*v;a5=8VtMfjF2DCmAevS>Ew-5(Vp>V3&scRC^Hp}*=1iZ=t! zQB3+>x!DD~ji|e&s6|X}9ggaZHae~PT@4-9Xpi}TPcJ>bw@0EjM$>G;k4&fQ&6P`$ zt2jMSbx`-!9^38OztoYPgm<*srKBr=vo?a;pQ39mj>O&|U;4a<0s3@7?;1ooOIF8U z2;k10_@MfKpmuXOouI>hVEvP);#~3&AMEPd0@C@599MX55{X|olXl(I+nB0QE!ii< z;4C>6!YAYV{i3R4z?1#(N6wn|_L`xus7;Gd)F4mOzr!0S!uD)`KiDeKcDali&XG9$ zLpBMbHcT>nzR`)Ww{`!VA>heame-wKS^LI!eK2{V{o*NhRsCmz^})_vqyNvO4ui;; z=4;Eb?#gHJtJ`iD(zI`ONYX29L3t|Ks!3l9+OmO`SRca8RMkb|!|yPy;|=g?y7!65 zf=}5?m2$?`z+SAPFIyPxmqWztiEVdZO4?rU*4l6WDfm7lV6fXSGRNz8BtHkaIxjvt znKXF(S{evPBQDS2ibCSnc(~6DOb;&{aNfF3_hCtj#M`ZM*SWap$8cXr(cyT_d;h#R zwUxz{^@Y4MXSLJ0&~24Qu_!q;hb13%Xfh{iYxZyiy)?Hudn4ifBV-`sp=@fJY5D@B z)YNq$Q`+@Pvfe^!nrs#k+T;XAtC2W*aBXXm+v%uQd|W$1&(PBpp1SdcR6Va!8H%}+EpDwmz*shZD)}t0Pq4_CZ^o>th z$d%A2$EeGg>^~ui>(lGm_et6`!^FUMl!(WTS_m;N-<+32;q0y$m*W~GQJlFTI-2J* zp$^fA0C>R}ILl@%WBw9vlN-oW?rOd>6c=z>Q8}Z3y^EvbGJw^OH=>PWTsB5u-bABCpZm=3>U}Ca*4M$@(YA80{81vA%?5`xZ z+GjC207hLtz=wmToJH@2J4CK5M4A|SOi3-;yNs63PzuurKL0cMcy>9%&>|+?82IcJ z1hu`(B=?d>n#nDb$@Vx1*b>*)(fQVK4*{`oD<{bOn6f5;%K z1mQQnYL;pb508s+lea_+S_ibFjCAPopO%(Zz`1+gPQ$&)l!l6kn2`}_u-}bZsuiKo z9HWu<%FWt_fkv}Qc^ zc<-f{y#8$Ed_0W(JDtG~aR<3MY~agZ%SVyB^76lDAw5zqYjKuiyddy@>y_v_XJ4cc)67y%wsMeg z*UrmZbG5}#s}`iZx^uhfnUigMtN+nnt&1~PM^~x_v_i{dB1S@}(4gch;ihEi%w4$u zJzFI0zCGYNT`2j0u9qq&FJIvv`0jhc0`hn6e#`H$gE8vx*cxZ+rr>}kUb4U@Lo#%m zZ)>EY=kO%T5p3hXmq-WS|D1EaL-bp}YaMvmDCQ|kTZgkbMcgl#^KiOs{$<>|zPf62 znzxOPj{fkO+mDN>`#pYnd6|>K5y@bfUGenobM5qV#-g{0?93B}kGGDDQ(%oCWkqA| z#2y65ANZxfyL-@vp#AZwd{i;G6%7KKC3ANiU%2o5F)M&aaN~Y31xLZF(@K}EVPIa&NLQ-mJdYE6M=C{Cc#A?|EwM_)t3G2=>Rfy7)E~g)0?LV`D+30?gGBv2@VVn zUf!)ayI)W8&H;TJ2O9Twl zic^e}#r%|Kr}+N8weR7M_yE5$1)s&h4IIeQ{&ZaV*>7Q?>H9+T-I*Y4u~8CL5pqby z*q`Wc3$PVbMc-jsslM8ARL}(AvxRhkm}-HEQZe|KM`d|eBh(2La@gd-FYI%rN;_}Q z5PC+&Xs3;i**|L3Lvh49m0oDsLz6A6&qP6YIHjxWZ7NSE0y#x91cw!XIla!onnDIL z+7LgCwPYMrJT*J)fp*3l*1bzxQ;*pDcM8zn8TKMcp90NlX>;ldRRnMc#bIJwg;?$6CfX zr~M=MiqBw$;AIgPFU}$a(5GWq2Er8@B=MMG%VG~^Jl7fV=O%9@!R&u3PsRVueGo*Y zVUC2}y3LrxuyKu3BP^=LIe*LJcsZN?({c>(b0Q5w;d_O`&}$ZQ;-Vzso=T~J4w&;; z%qnW%a5@K`ur@R_R6wsO`npAzo5NnD?Agub&qMgksoVY?FDfbu`2GeH`tv6d&?usR zYWDOL*;UYxl|UTF(0vkdduONe)yT_>$GS1&@bF10Rn&_)ZF_tB`x*-?>yUM$7DJEU zBvCj9@z6b!wkb!(;an%L{Zfs^On!)s^nOJOx`p#jp^Wj~2Ah0cU}G99PF{&d9dW%q zrG_=+;EJBSG+6v>xW;}hp^E)>t30=!fv)Z4IQQkbB<1v}=dOu!p~i6BFKCHXwW$ki z=$M*2h02dAhbZ%Oyv#zt1W@QNhbxj2B#392@thc*ooYI-$>cmIF;H2Y&o1*l;4ap%`ymL57}!ZF0J7*;J0tOTCP&iVD53 zaNP5GSA>p9U3WtDBVhNqazU+{{=NA_j!<;Yx{yMcb+RjqF-S;9m=)Mb@nVDHyTXQ%p zFL*jF?OjsNamCf-=;Lg^;LUoV%z^kM6M}vsa%3SP@th2V4Gu(MBMf>G@HSO`?B~Dw3oM8nC4DH`02$zZg9Y^5HX!D{>_V|70m(mm=1g&jK>6)`k88*peXJ zQ$*0HwHOj~sN%-wH%&794bF50~gJl7!q>(5+1Uu%2rh~iGnq}0eK zf{ymSNBwGO;NWQF$aq|L*9x*|T_g1G=ToE$n@(je*ZI-W(c-@_Q((yL!9ZJ50t=_- zdt7d3V<=rwPFqB8Jn~beE9N@!3vik8b3q3??fvHQ7K>a0_s8aDM9gF>8x4|;pSlVB z`PeuzmLReQtrz@*GDdpYnk8>o#ozd7e0tnkRGt?~HwM>KkT)C76$G~ZDLExI&9d18 zG^urZZt9JuOkusym`tmBF9}SY_>T0vVB_K0UJ2Q;)!~pf{8%Ks+vJe=V)Tt~e0!uzr*Pl{JeNJud84D^VEwT9V3JzYt@%M^&fCvYA*6Re48d2xru!t&b@Y zlkdb~WU?2Ye)96j8sH=iJqhMfeT~ocy{K;X+2lVaW*DiRVBYG=p!t`uk@V?amrX1$xGnr;4N!Wxb*;8K3l7}i_?Rg=8j<478DWG{#C{-7~al9ieq z9oBM#RoH=rM#W-h(D2slawhD~1b_4>K7f{eQsy%W6j#w}vzCrJs^DaUSWTC37rbfc zwS(jxD$w*icTy*p6~Zx1_i zqd&rJTZ7-3_)(F$7DGO5?a5it!g$NNMT<* zn}yiud!Y%Re_U;K9`3^srJ|4OK*I|=q z91u*9{6Qo#Up{RB|PMsg@N48kK|3C!^9%<(wK6iYzj zy;_rCyPDpt^x1F6*X4=}|Lp7MqN+wo7eRvr_=N?7D+T-45f+f!hNCnRgd6`h9WwD6 z^lCVJMb+i2&?LWjU`&9-)m~^Vm2NOg&d}h8u zD?B1{1ALWHqh4>7oh~j~YlZJ3c#oY?5!p5xz*zjl7-C=BC0(gq{je25p3Z559!JDo zs8y-G?hOu9%;bvtk53s*qQ8ccI~>j+pnkLcCTg#3tiGX`|#dSF%?bV$z6=$H&Fp+ljP`C&7wiyoq4? z!K}cL2pADnR$Vmi>$kkRLYD13@aJrGkE{6Jy_w_GDnFe|aQ*ia#(eq4d*>O?saGeY^hcwUS0(*hP*4yYj5K1%sAZ{I(Lm!>`xdAZz}uZ* zIl-xc^n9TP3Jj)J0k1~6Bq*2~<%zcpz=~ftj4n`!!E>{vy`)RiFf_C1fFUq^o?6Xf z&xVL0w9Q}S_9cpW>L~zg&YXV_um9eAGn@&~aR#qhFOomTUF0TU)k@A(MkFoiIME0( zCE&m67usf?dy~z$BT0hcjlF`0+U{#MfSq}Nd?$STuv@{GG)R{)^E)0+c2+B6o`&8V zo*#oqD)~q{>>HWa@%9TTA%1t|GXWvEVFz6a6BoaT1$rncM`4r}Eads)Pq%(#%ZiU` zzwsSX2v3r1s_oae{GoHKSTZtd{ZNMNtGe{u7IYXQ%Psft^bIkDSx&Yg_H+eU$r@I+ z^b~MkLu#wv&fT$VL#yuls-ETb`pNLg-v))!cBO$fLr>2(7p;)HQU{^RC`PXDELC-q zBlFkzhJ?_9hofO`%GFHor;qkJzqcUX;88H7dv0!;$Z@S{J7zk~mr<=g9nV^X_ot1G zVy#vuyz+go!96Jk|1Vf6rJFis$vWpSLk#U!OqUE!?a3!B91uklD)W{v^$D0aA8um8X=Ky z3rREKHs;j@KNSa#VOU|tE#4xk=AB=^FlnYiC7*;%uhH`6Y__sa=jrHN= z9UdGv8n*6v$UoL*KU056aO34tbZEC);yF*B8S{CbBd~UCLHLE%F z$o^=g@Oim^G9Lvm{07hZ)3kv{|C*VcftN#i_YS`epLMV95+sfNIRnl657~o+ zs(Gd@wyXsUEy?Y>*ZxxKY~+gUx1`Nz7>y<_467Q+Ja~-~g|eMXDBwi)odvueF>;WW zvHll+Wrwz&BFC49RX6ZopMYXdhKNyZ@uX23Z%7bF*+S=9J^ep-5=FBhd|O|Sv-H1vudC{s9=K!iv+eDD`=UXj?!00W zpxe~eea^1?(<>CER!dA6NvvTvg;_58cKe>K)&^}4TSjX6mih}5d%K{MBq6^*T=3*; zx{=irL>Y3C{_>m?@g9OHphwMRWOl0?ZM%9j1pJ%U4x^-Ey4fsLew2NGx*9UWRjTMK zYa(bcf!HJ9AucPrRKZSh3_c$)a5fvbFjLI9`bDo@fT-I!NyWWNKXM~mKhr9O___v| z>rvvTcEl`P+)H zY;KF1#d1aRPsGI^$Ty^f$l^MRykEaCojQ)_j011ApiC9gP%NQFiG!LNR(}MB&F;u= zkJFhDP?w(;#kb?oKvIE!4HPCe$2l^>Diyk42Tfas*aK9l!!cA~6sQMEL)KsYaJ6F= z?w|9|H{{-q$2n#6`H&d|TL@(CShGuEcL%)wRD>Uj=8ESt2qG-if-x&bBG;*Pxx!vf zVfqkmD!sD`jGolE-fgx?{BkHuT^dqf2;39Kv9>N}f`x|980DQKhH3eHy621qdMoN#Si%SI7AcDe@n8#tc<;2c7W0M8RhE7);F>tf_ zBV2b$20U-RYIl;3?!rb$9-UWHjKb2ESs=%}XgFb38?;)_mgUWqD5O=mp`ckEOJLMn zjpLi-%azSVt({4$;Y!B?OUW?28Ca8M0e}ioch#_PI!(=K)}Ix2wuh+%L{6GY(m2Q! zrK!&>r0P1u-6=iHpq?FF`ayLjHm z$SH~Vwrec7jzM4+ID-TepE)^v4syIJlm8odWctY-eG{899)>=X^9lIz$?*Gn-1%(G zgMcT@MnBigH?HHs5+(dg){P?))^CDng!39A!KtTj6RzMIN8%Qzx0IL;dTg@&%@jSI zM(<~y?v1t>mbL4V5dpuU^U!eM37hklLdr2E27tZ+fSGS~y;F%%ciKiuTY;Ch5rneg zsC>s9kajyc%+ckD$m3+XJFRkY*U)o$6FEPpn8Z-w()$L;;JpjoeU!OtZ_8GmK&cg! zXdhR61V4=;-n4Xy*>(l8jyJHqfrANvD%RTg@0o%Tkt4DR`}k>HqCWEN zocS%S9LRUVPci#aYKGj*hP_I{Z9M^Y$z7H>eX(r2bs+vqI?Mpm2@I2*rrwv(fEI@} z`O2X<7Xs4StFHd@%`}sst+5cD?YcBb9g`R(38NP#7M8O9_YHD@@l{_;v;9(o+k%SF z<8IQ3Tl*H&74G!RPuJU%1R=nr!59%*Ufwhra{uhw6Iq(m=*VI#F_Bi4%!!kPCMxw+ z4fE>_j*N4jq#tQy(t5T-CQaG4%e>O>x$Hq) z$7dpMWjfsKzrHkC>qZd;<9KaF+rN<;)LN=(i#Hj{v5@tr#AM}dRB~fdYuQ5KO8oz~ z7NFm<2K;aS%&p-Wih*M4|88}SxGIe>Xnu*2@7pL9q!J>#jv^U&a*lLN0W_<@s9SQ$ z2Oyg{!c!QCPaVnmT!5$5o31#;&8Z@zc_=%_>tZOPV>FS`_Y?0fm;l}quqhebt?`ubc^7wqN=JYV<&11A-=GD z3Z}U{-~HIw2sbzW5s5p#fLC)K1IBd;BUXY;ZBI25ZT-Gl(c1mQ@h2QIvfjr~@Y1We zUn4%A$oR;}=Fj)ngqfMI(J?V?K{vK}+&Kx$pw28WQh}`sT32h13{~7D8{kO@44zc4 zB?oaJ=tCf@Tm##AymyUkB4DtweKY4%I4Y6X`X}J))Y?m%IU!G0RAC{>!ifJoqExtQC#l}P(4k58JfT36K4IOh+XCoLGghm$IXO@um}}G_ zr>XTv#I7iPT;~Fm9+mTjB+r+UjI68Z`?=>ZuXbi7H{BrL8U?MC0H3GU$aD}1EmdhY z@(v^Lz#O)RUyC+4muo^g%piZX8L**UJ5>mCLC+X=LvbfyB)9EnqK(rrL|CBGsNbVh zSsO8AU;7K|mqKRITELtR|C+#L4BL<-$T>F@Dq=xJ@oVLqJjpas9;jPr2^f7s?3gRriqWKa`(bN4o zD%Rr|3=!RLe&U2qT0Yw?3V?#6#^kz{tUGxqhbH+_wff;-d~B?0jqD@pf4U?OrY7r796rzZGeN1znH&NT;Y_rPzw>@#*2`srf`=4d8W0NJ`Mvh{az5& za_f5!#>9LHM~f`sC*rgjgJ-K{_O-saT?4XgSDU`TOj?9()em5n5i~UkiG&V&!hcLW zk1jN^Y4?X-Uet*xUT3p}KFtQJN9g}iOkUsJ@3td7&AKPD)K0dcJA@etiSme&mTCCM z*Hy;{=s3tXopI{4q7O0I2R8pZquK9zz_NHHr3md41{rcH1!Kha%nr*uWT@V>7bfpH zf?z53b1yfw^!)Mt%F4<_I(ygD8Cecj6y12*^>R_dubiYJaGgqn#@S@*MJt(bKo7?8 zOh~aeNQC<5UlQEtk|R74-)nw#oT7pUOm$`W-wj9$M4^Q#Vy;-?+P|pG`E(fyJWu5Y z!d@#zg^m0fqQwWK(tdY&3M25=7P?BJ7GCgK7hsj7yD(wX*?ie0`DF0fT7iqOk{9PO zIKdPNS@?iHuQ*-S{|fC68QrP|G z8I1kxymFv7cAq#aXEt!r(zf@?0z@l~K7G{osaOe7Qg-S~Mt}@E@aQ~ueJq-WR%vu7 zH1L3cHV!Y`4MD~+(POFMfZMHJ-si)5dZDrKeOgwSoahE25}5KYK5qv$!W{cg zCvr*()%o_encBHgyIdB=h?GK)TtXq_VJ5`6*Zb`&2%T2G6I{+oyN5{qrbs)x)l9F`+ zD{fR&$4)T1PjlZtR5=61c7Q@uL*U5FG?~jQD_#8-8uJ$)H*$Z}A8ki-Uwk0KSE3I; zW_l%5&CMBnoXO-BQN?UFds)UgT!mRBezTlrT;r;hQWS=M^GYdblmBfAH|bC^;J9sD zBRP`)o5qOqW95RM{%+Wc!=i@a8(7m#RL=DN07VWFoqGOMT6-gX-HIZF2zLmHJMu*W zj%NLqYu1QO7;F%?!{G4M!5FfSCl?b^hyn-8Bg?NeIi zvJI`~oA2QLTOJxA#~-tF?W8E_s3;QAAmA!QzsXkZKmgKp0D+Q~)86Nxeg}5<{^feK z#pzqeRugb{`edoQQ}%d6fbmo0_wTn$#Ut{~;C4e+MY?N3q%r%O#|((O1iVciDyr}M z<5i7=ZwT*U0iDo_$V(H!dzA@V{nYjR8W&3nltgBSbUm(n?D#;fAHa_F!kzs?*VspW z5V@dofhdSw6u1|kOz4{)heVw*Y9u=`@Biw1}Z$_A9 zB2yB9C&s#-0YgLI7InnPw5ZwvSbzk5+uzu*)~k!12Axzy(%6`cc90f#m*7`MN)-1< z^x7F6r-I7xo>%AS)F>S%Qxf_k>DMoGP<|ll61KV=#k^E`A!V+3XoRvrwt#Y^g4kW2 zvGf6!CJD)1zCs3*PVo{6vGWr@2h0~PSm>|hN#&B-9squijX$Tvhddw>^I0Y?+s}KJ zf!!Y#f>6igKcHkTNZT&u7K2?7#9S{!hzX)YC#|VH*GZRafvKl(?tD+r-z{anYyvhh zlP~xR5#kCwdwNH#$9;LRBvw6Xua1#O9&O1S!8zo0_Epn9>DNyuHjbhhigeb~LG1aS zcV}y9&{ie)(!Sie6FRgODB7WpK3(@a+I$!j-z0YTZeiRC5(E0Ls z($Xkh!`)U*#r4^?nyqnTjv&d$;g}@$YBrl{!AQ(6Yfl!-NJ_%)41;`zp106c#{Ln` zF?9r?5^}W36aIh_Wv$}vF{~YR#%Du9RY?TDljyY`*7pzkwpl)5inN&DCFP@&z9qX- zXR!>Repa6Nv1J21%Wv~hhh*HPW!EAV(I^(zhogY?>n zH@;46$M$8heQo$sjdoo!KeOZ8LPD8MO!2CJ8p|`re*?f9`}s$Oc|iF6A*`iBtHF(#dI3l(aa4 zB;AA?NS{`(baNiOo&!VV$C0pK;{ckJ_eC`$azV#@#wn9&;u1gTD8D2+jjFHwcc8s% z+!yrg&XqR&P?E*$D+N3%^tt`5CiJ}6c?-4kkT#)^nu z|9_q*Y66e##I^&3?6XxDT`&@X{V1AYsp9fp(B$+Zg3QGlslkN-fKVo)rbdnLQ4XV@g43M<`- z2CfNd33AqctWi(H`=S0f8YVn?S%H(!7Yw{0Y<96An z%*)RBs>uF-KUl)ML9^Un+#JOy91t1O-$kL^^2Ae(27QGy5$G+qep9B zC=5@{J6eHV)Ax-<+k4bj`}`0W^15Kd+f1k(SAN4vxC8)hxKkx@~!1!~w0%)~G?A1#JJf1gp_>^%UKI-kM~(LTFzn~p5}zYl)V z@(qEXUm#YOskL%k;3V9}&8c@E=829*mizt>pzXLi+hODnb$XQ(FVYJB)x6zC`Mg#~ z6JwP%FPgGqXmRYDB3&Z_knjh~*i&ZVPwmzZlON1xpsZ-x-fTR6&{^~bzD?{1-5qL{ ziu9zDmcR_KcV`C?V2B9+v?-mod~=oxUzq3THL3hvz#cK}S&;aC(y17HY*#9L%cUa1 z7nNnAD^+hjv2?v-ojqRr9+YkugHe!knqfa-_QAUAqVV!Nkf?U(#P`*&1)IQgY2nrL z`Yx!$_tVK?N)T({VVa}lRm!C$@$d3vMlFeJD^jc!P?`-fzuN;vSQs2sWKh6BXDM}8 zBuWFod;%wusYoDJD0x$lCLRE8Ii6H#05bu>NBC)7darc16G(O(p^Bd$B58JTk!TIC?qA>*S(iaoAll(okZ!c+;3?dU^ zIxwSZtUSO+L~v_h__JTG>8QKOFAggzqe*F#oESGL!I5ow4zB$RUkV*?KWhSBqsiXP zFPXLiDJMKVV!q+sA(>zdqR4B@SsLN5p_jI?HsG_a8siLIzbw4lCP;~iB*Yg zqG5lZ4mAxQAbA%m2@|*97=rrO^SU(DWK9GehJO{(^`=RODR;`n%Uv^-tW8h7wL7c#rxY1I`~*}c!vsjbZ~`t;1f zczozN#S_ZwSYaZ{D;Z%)&M)uwov5KIKYfCMZjf41`@D*ta=AZAWo2-RVkmqxNoC|CZ@Mr2U7syAJ!`zjQC&5P+Apym~D+oKT(7^z~7> ztHTl*bM-S8t=votS}J!ZA6;gWX)%jP8D$vVZ&V6-1{MuOGvHs{J-5jjTg zZEeNrff@cs%iWv>lxAA^AgxgpF;_XPtQH3v;by%YL{v7^?LKJi*bH(gAfl6tghI2f z-5ghFXsA69L0czc!+7X7wG6+<17chf(Pdvuw%$ zU;BHrU$c*iJ_g%QS0VwwxYI;UglaH4Ci2TOOFWBmm~|k%rj9bX8oqq(W1U zBM9E~K|`U>7(@5fQ-S7#;^ph&u0P~eh>Hs~Vot5nK9B#Jd|-IE(P2g1spC@G3Fs;m zLl7u`vRLi2J!%&m_C?;VoYel8mQ6#bBkYR8m$}%irN@uFGC@8uCFi}U5KB1z@Tow9 ztv8D=Y{EQAR)nlYF;m={)Djlxd29?;JKhGeJ=@}iG_j9<#(x1T%nJ__EDZde`!0(i zr~=-H^0u;{GfN99j=x|i%^{*86}d;$SdxzX(aExDlxv3OyEg$oS1R(P6-7Tkf!^L0 z0UUyS10-04HK^tY3e}SGOkA{an$XKOiYgUE0`=7_6{BNEcV~O?5|nEgM%*3?Mxs@M z)A01(a}Ibx_kKAX1qB6s*>@jAtyEHwwNz{W=7`%GOJIM?w<9vfz?55LO2)$xpW9Hy z)S?FXy;{(K{kQJhFWl)bTM8DB*SjD0iJ{$T{n+1ZAv+G6^qb6bGVWDm;(F&yk6}H>LS(M{F4SYOIB`w{y&V{lz%$ zf?>2B%O7C;w>!?0kmWG2@|LE7XaD5!X(A5sKQqbcec>@cWKF9K4R-U4@c}<6Cf2Qp zGkjsH*2$U{x<-Zm#6p~<)FLAz`S!4gh@vO0i=f0dl&zinYz!^Se1TU~qIuAzEcmMK z;$rF3)#j{s;H`EJQfyx#m7AMey)N^DsG23AT%wCWj-<3CCLW%iXq?!!kEm^*4GZN* z@qcJvg`{y2DaZPSHGR;N-Qlo(wgr()4w3RIqdq91VRAyYXcZR%jdfroH0LP$<|xH+ zWTU}S-(f-|>lWl)UD>0VPhjCGxL&sbG`?i%e#~eb6UL=#@IQHpT zdC&7wTWk~W1A{YPsBP`85jC~c@ZjLk{L4vWY|5tp@&gz-P^qTU`wt>)Ia`uui_vf< zK40Im-Ay>oR$NqN_9qT&E$z8IMk2{2>Nf4-c%7{tG4Xni75(}CX#IQCQXKiOF9IB# z7R47TYHFqzQZh1SSy@D$tT+sjhYtpaQBS`Ks1iI;L`^k{ek-=oV6|U?(cbb!dKYVZ z%!0Q+z1o9d7z1^8iD+ff#^yXc`&~<*uriZ{MA|499pZgC&70# zt)1cGoq#Ogq2fqjOI2qb#_fElNy(;cR=(9qH+e6(^{Vm%5EN!yXtvs9C-gXg zGZI~e@K(IO+GlS>$@rC~FjOiWI+x1Lrz*rJZQPwp$XEi0nHwaSH|7I$zpgNk@GzBP zqFhDd#|Bs*`6?U#_!scCDCyv0dxz=oV!WC+4=2oP4uT5=G_v0(fjgBmicAwKXgt2V zPn_Ot%Bj3=yOr}kx^^y-pi0`RLI4ABKCGlKdH3MR z8l$~qCwvisEKkz&{gryhI@_>QgLgr}PK-;32CXOPwat4CTQO|QzIx8^3n@TMFE?)p z#oA)Dms04bqLFB9bcc;Tk-3W>Mm_dm8({mUjy_oQ5B}Lf-dwq0zu&+Tv*@?v zvxClikj0TRA&EDmq)~t%Cw)s9%}k&MI0)k~J<-L+d3O%uS}w>7CdUCja7)Tn4#|G8 zYHPXP!dCxQz%(F0h~WNIPF8lLT&mPgt7-x|V1I3a#;m6fz0njP zy6?V5-gip!X_Tkfu@<_UkS%hbvTAQ3(~Com;}0NPkBmCJ|G@b@xjDpLtz|0^a!{@z z;yzu&Z>X&wjtQgW?vD?~pRmShUFVwBp&PK4NW4h7tmz$-A3t6w9krz5Xt<-Bo)&rj zZ@6r}@}k=peact(LGADTdt1c2Lk$V}kYZU>Ek}vU*V#CtS%y_F^wVP$`TOe4VDg|z z=gtK6T^=l=H9)Gc%vUo>8LEXq=D=G#4unal2)&W34ahg8LLMv;w3J0SI~A@u$GyOp z!Z@yC1=;4G0H+Q#w~9)hAiC%$B5L1X_RJj;n~6?z>?u54rLkD<;udB0%PBe!hy9dc zuiE$>es-TZbdf)MqxT3y9xV6+U59(<0&}7Mscd0jXvpQ!Dov}hsWNkD2#r{`ao+~I z=Xs^ExaX|2KL}j~j7&(d+58)_uvA;|0u|rCK#5UQGNHCr$O-Ya_*~QXC^Lu+wEiNA zSz6LS9VI)PgoA@q&Uv-z&e4a34e$*QW>kU;>L1BbI7T;Tm2O8RjBGs)`^RRK%trd+ zE_K0(5m(QtP9c;5e%)*yfL}raex@vJyZRuNN}+UnbN8`VOD-;(PLy@>@i* zpl#RO8`6ZYEiq_vPJMX6!J*FxrMb8D7Lr@~pv+rV(KIfb3qWVvJ7|s2-Iusf+!2sX zO?iZ{dD%zHtk`=F> zL4{ee_UJN{1bnihGc&5)-tT;yGUCN3v%%pliT}1Lk@G^L%pYD|eFSF9s3H$CZ6*Fh zwo5i%;-1F3fQ**0fZw8>Jr#~q6T%>2HPsskWud0P7m#B&x22~$izeeMoF1O=!_J`1n;^N{ic9SOT zg*iD>U}hqVZ+c0{H|(SNhz+AN`rnOY^PSgE{n?*+0a3$dR`|7jV7W*o&~za#{~o9Y zIr_q-f(DA84fvvS^Ko15*D?ES`_7c0oTB0`bWfGV`yI1T2tORsYK#)==8Z*Dn<6t= z6<<68OMj9|P^!u#H#&@^O*|X-@2|tBBs7|S*`@!1u@_5XdKpH%T6Y6L1{RjRC$$~j zhMPGkrp7Nsh;~n@G_I&n*VgWly>fMRg^su9b_K;Cm z(XS>P2fZf|l%6$N?E*+Qn+!DdZixs_>&-~%TkoIJ0x^Kcm;e+IL zwzoX!*)xdFt8oRq>wm3|TI7pEr0>FSNn%eY+B`H!Yvf!4ZVkzFf6L@qt-Y5}LG&LWwv2`ruu?`Y@r2}WO{Nw#7R$5i>#msO3M_pK3sjy6+I*cS zXfKLDlS_7S8CFJ4lyMtoQDPTt(ChJ_L^1ZwlWue>Nor13de0fm=I*^A%JWY)=6<^&k4u1Cq%+zq6rKMj9ykW#+(3I$-ba&#Hr01gn zICL-8?7>RWVGhf6VZ}&Rv+G1(;Qhbn0g+&)93~$%zU7{03b)mi;>=?=B~zv2-jR=v zQHfW}bvHk+M5=hULKO|cYwgaa92}-`krMlow}Bcs*#?hho~&C0aW<-R`5F}p&5-+- zr|*^Yr2TFI?*AkR>b;#s3Vma-+8P@^zsjPNjq4qN^P)Eo=gKg~)7b9y3_m~}>|dEm z9?^+VPC5a741KX3k#;3CHP&@7Km9*!ePvjcTf4Oqg3=`o(%s$N9ScypySqiYyBnk% zq`SMjySp2{$9M1dob%)J@{boT)>`wKbKWt=JqAE*9W?0Dr7P)XNpz=$##{v#M1gG_ z@=-_OzTn3Q0XuI^9E>Z{cYVxTWCn7!w8}z0f4_AQ?kYcut8nC#Wap_#n1XQHTGS|2nOE>H8g8I-BeA?Rl6F(fnRw)`z zlXF*CX=0Z%7J7N6>#q+=%U13>z+pt`*h z{sjL0zVNs_SMGeRfO@*X*;-W7S+nejii+m?K_L-8BnJ`I;5PpAmnrmtH$!Hb66cZ7 zh6Z+E(n|Kp)60t&m|Me9)v$#G^g1-077bBRmoT}h0?+F}e_Cu_c@x$9*9X|gZMSD4 z7YZ`UK<4TPm*~G*#ouKOP&Lx-YRxxajaWz(V z5VdL6`Tb|hQ~E2cTL*rnl}opsV`=v>D!gE40>KISZOc>r0(wY&{ z6%$cD-5e^Bh#NaP&h=kyDO(Yx_vZmh7EIUGHoYV#&x~y_B zN8Q}3YJ>XJ-zjtkw@_{l@T4R0RxttED3{a8Mf@$F2Ogra3 zrj_jcF;gkk+|@w3-!5+vSt4fZd;J`?m7SON2-NUg^|KAHtUYu-BV3;U@$dGiZRFtC z5DH2p6R{fhorrAr(517uREZ-`s_A#uwAJ>qy?(Ue^5|XT65LV3VTHab@AXlc zcM8={%JpF+?XUuBkF}r4WXaS@6Q-L;IaqzvUKup?uB>eQ@oq+^-ydW`-i3Y9`9AbK7; zd~;vC5gmvJ_OUx4QfHD3f4W)(=7a3cKFaRrCTb5Y^b|Zk#VgWd@(Ed4(Z&!*XJm}c6e%sX zxH1tC5HMS&^Z^6&4HwqvDjJOeJ<$nkar{<%rciwQv-54DXU^dDky2!WoL$b=Eo0{e zgz*NDkUnRpC*S4fZ6#>F>zOQGQ~tQa;eQ{`*e{A)-wmam#$0STo0o6SC+Yfe`|WhX ztj*0B2{%9DcP$%90pd52sq4DHhNhx-mzd8Xutk3|O*{2I7Ic4qHkT~VMvz01T>6HW zGzn9Wi1U6ZqqXY(5h3DSG>BL?*u;;6J-y9sf_#gN6pQ=p=z5%edn*!>|2bSERD5yr!JqdnIrQf$aafl1;z2;*4*I{ zn=!lE-L%hCg26Rx)()@YugH|wqKhm)e5b+ghdARGBl>J2gc|)V>%=_ofy6U;!`%P-DXub>z?;9X{F{q zK+6Fb#%XmdCnF^M1;Fu$$jHBX^?EJpfbP`bFjrrdej^T7f|S-8Lz*m^Q4M5huO!eb zgF?V71Wk+{A>!d_ZPrUAP;Ub4-26X(Py@R=*IM0OZzjbSYt7MtGa&7GK(N=&mPrK# z`;Fo9hBdeNcCj2nb&eN4C_#fahwXGaH7YGZR?p-VB-W^aRg}P8XT>Z08Y8S`I#tEO zuw%!p`rx!KB{gJbD4$dBorb$?KQvq-w%B$JkubxI_1NplOr|}Q9=Re}@N2h)(HK&U z=j@A}-WQn>e*@gvAUR3GL1$uQ%aY%M1JOOA!>D+C5hZo=sN`v-*3=5jvJxaLEb+g| zsTAd52aAe+=jTt5Pr)YC-L!m5LorXN-U@2Aa-4Sxn{|M%ohpyCr$*4*(hD2#Sld*Q zp_io#W&j3IpKL1frcOwJqrgNg(Y0$gSz{~*oHGDH{cw4C37{)Kfi?|o+k>859|;dk z(7Sc3<1E{Si_SuPl|n2o&Nj>7PtTgqeoQUG2vGiSL}>qN!HONp z%^feB&UrP^tyH4MH1i1@3fADwdp|Zh*=H6@*u-Rk0-=N@C~O4wYsQ_C{s+QM{ z$EIM@exlBW`*@ZX9MuwI~3GK=M9_qsOTCW z5SP|YGoMy5uEf^7wVQ>c2eIEpZ{dui>pOd#eqUJUsgwQA0Fnl+v+MVEq3ebKmlj1Z zD^~)V6I;`GSCSSA*-mt7rT29q@oy8vudRDg*_xkUQtmHfStZK+L06w2e0bhBjSNch&SyA{r>imuk;~; z3JSq;BG`_0Kj<&04M0P%7%twO-X*WxMEZRAo-as&5jf?xLgF#RDa0&BJ1j&JQszI7 z>)0$`(j(HFKVFR1uOlHN3j$Jh$=t4tK*7f#7J&nd8+z#g3OnL4!oWCCjdA8-j|q)?dHcSI0+=QME{r#PTsoCAVc^+HiBmCI`h=Nc}o*$_)i{p1nWZnKkYP?(M4^gA zMP2|Eks3N^>RAE4Vf*RybnR@+!^?Z*Z0+p!(@I8W0YXc9^8J{{-gR>FtB2P0Tg$rf z`=gc7ZPib$t|o-N>#-^PMWwBP1yr|88>IHe7YN0p@ZaH{=XU+L+KeX=Pz}V|=8tJV zin|%02E$9b#@}0Zz695bL90@#CZS-fu#FW{9FvgJ*At9l!3$i6l4Z5Mdl}U^2hP(eCtLuxEV21ZV{J|$ZlxB$Q%!xHG>;nym|CM#ifRYL{>nn z9Sq?>erfOm@9pgkw=$>vQTHeDZc1xO%H5RiG|yAD2A|jBfC1CS&v~O=^A{rCc7G?o@Tz8`v#VN#~*O>bg!Q8R0{3uyA~+)R_Q zJe`GEiR@^5{CE*84r`(yb1S-T~w*)>&4P{Iwa$8 zAq7J*kCR&=l`@^82t)WEm!qU))bzo_@OV<7@I5~K1Zo{E zuSdh;5=x4d`ovdv=bCyQbP*^Rp=)dTF2*QkWSzX4XgqY}U|~W)=`}rYcz4SO3U6cC zy4`TG+7ReQA`})&3JQV&Ei!EDmgx4?cQ$|uV_+x(BS}f7NuTU@zP8n#y28jL8hBY_ zyy?+4K}O#kuSFbFr{ujPrZjxJ{bOcb#k;YUf$r#>??wB2Ug)Vie3g59B36C)mqe)g z{xKgy-(TRpajLU_p4bWFhGw|)tuTBlVSS3e>F!<}f4ck}Y+$$#@Vtd8v^YF8Za>qN zdWZAfdj`HIUZe8F>uR)IBr4CTeA+U_5Hkp~Dyk6s!+9+ZzFj#ZscRpI`Y`~R6|T2% zxW1VDi>s?EFcb<6qs8OC-K|LFYVq*U z0X?VLk^ETm6}2d59`P%Cg9@`fJ;6Vc~tm9f1Z}_Q@x#>BU zzr)Y6eCOGJp}X5d=E9+|r8jIeFpksvAW%?y#J<chEH@Ov;p8xi8!(d|A|~u z=Z~gpUw17Dcjc3=)$ujq;T#U4jq7;3-S(VqOZ6kD9N)H#!w4ZwimKPIE)<zM{015Ny@*k#;6GahS;FCB6Y6|e@*rVhkndXX%231sjA1A3#@^0A1V;f-`1o5Q z0dfv-ki)PAjSwie{p~6FRkqur0C?~65G}S$q61Nr$lP~R#rE;yD8`+<=mlA*R{qCk zuJM#an)_B6pnp==RJvEV@*&$#P>6gl<)_#uM+$i~NRk^|@hXz*oO9Y`-ssc2i1_ll!^z7^ka0*&+-T&?t>8IP%;rVrs;h}gcOl}u?;OZ^=O5K&2 zYG`O^vGfP2VqR>0WyOhSU0G(k!v{PrE{@q;_50awKmLju%~uaEJ|Z_5<+58$59#p; zdfmPo$imcBM9!)Je1FVNpBgurZhF|Ww>w3<)9xVq4j843zWfD#h!&QV+m@eoZraf8 zCgfVo8LS}YR!wFtg3b?_(1?!k$PkWjA`0TNl3B$$V6eU^M`omQ89>(tW_PspYN$l5 z$$^4}7M(DP5cJuQrU(WQ@8_`m_gnuaI`HC4WwY$qEheqE#xpg=T+q|IaBGcAjIZ>5 z*8-d?2M*J*zZ9-%{k9>97HV6i-SY%yDlAIVd0U^O66X;_ z6qZOP?gj?3#6*V=HRV#+v%B|cV8KN|LVA82bF5;^X5UVP=oRyRpm#A!V`mf0jkyq-c850ka0NA#*e#EpEN5_dg<+D8=qW*jU)j&Jc@f&qjU58d7I z;iXA7e7M)NAU$BzXuG(ie9Qa&86Pi_i=M`B;HIn#I@Gfh>Ar=WmX?%I8!>h5{tI zAV*NT4U*)s?h_P-4=R30aaLjHtxqZC7xLm{Ni>k*k8@LQuPY+8+N7qLZy^-A${>(# z{qiy`DKmivf2(qlt^$QT7;C3;kx@calX8(@lD!@*!H7c?%m9VGXv8Fm$PTWGto03~oXY;MqW$7|N35YQ{{$7vZSlrJ|V8`AXVuPFAu=u=FII2{V zCL>zti-h!sgi+PX%&f137H%>+E_jGSBc?>=^B!VGDy+VrQV$!6l=6@hC_$EtAWAPJ zhN|E8dRjB7<$ATf@*qtSK@Yu-T490Di-pX@bycdfQAQR@z?47g|#{ zyTt-lvO7Gs5h)~0=VUlBII_OI@WQQ>thnv+gIUS2J`H9 zaLN7?s&>ev939aP4|%Xk@7-GA6cb2^iER%yd}KMSEJ}q`+JNinYlFGQo;{6p&q0Vu zivOTeL6z(FC$AS>ToxZY=T9Lyz*ieQ=ldxj6i~nGBX2)f zPFk-mKl-_^I0zMwaExr_mRQg8cQML}grSIk(NWM`F=zPoQpt77Ds3;^KdZWmsW~59 z#Kz{@KbFq@d{U#~epc&{y@C(mHFQ|n z;nSi&2Dp*=Yt)iF`l1!=yQo^2lyU$0t;nB|Jc|I#?rKVkE;=KjPl~R!$1qXzD>A)7 zrqtg|M(kH#eu$sGsk+l_-RlP@8kPcmh+HSO+A@aUt^a2WH>Y??LGaLi2r)PRh;`e+ zjtp2KR65IjWGO!?yg0Mz>z!_vqaCCuvYP>JH)335+AvZcTEQ)mP%cw)sfne9|0Yf` z2ZR;ce0Xjbow;5s)lj2878b!*QUFa&xzk(pkk?tSA|uoNP2?y! zh0FSV5vvPU;;WWV4Q>u3G&}@N(kr4vEF+xqCF9rr=hKaKgEVFAB+X{v zFq9Ohi)_n2Wfd34ba}cD9;Qp_>BSuK5DNJ@NC2NQrzOqP-6=MGI<(;dV$n-s!6@cH z^Z~qH?SR*Ot*mPbQ_wGt;Xi57-}Mlgo+(73W0ryI-6!XCh)P@Y6ql40y??%Z_N-QD zCI{XlE)BM8W1`a+nD7|53+-Z!b%5^`3FI{7JH+JqQ9Mhx^T?Fu%G;Q9ssdi@p6N|s^HYE|4n5fWTjR1;AJ81Hu=@V5{WaMG82h?x)a{9k^ zy`Y!PxN#E`6K~!>8gMSsMwA~z6NrmT8_9KA0pDWDXceiOzR-zfWR3zBT&4yQegEV} zfA8jK`f^{ptl)Ost+la;P{v(bjZ-R!eX5;F5ZcTf&FhZI`9OKigX*qOQy2&kjFOH2 zXY1sywKcZQCCFg2do%>i%rIBmyXjZ`V1Na8mVx7{vm_-U2~Xo~6lMZ`>B$k-f`XxFf#Ln({4}gwCak-~UK_=(W78=O@L;|IA86-+K>ol-JDR2H$_I)xARh*L z;(!EL|Mq26Y&x!{OhLV8v3Ts3$S@xnuKvY_3uOsb!i*W(%l0|ULiH_`2QNMl7jH&S zhZh+1C8VT?Smz>u@VuvvYBc_q{5^)qVN}cR5a*E}I3U1Nzlz{MqjgxS@VJtfF&kN5It z&1hR`HJjMLbF@BfII!FFaoDn_v8Rt!8U{Wt9`uPd{$GfSv64cs*1!wxDKYbo9V{Q;XFmpkMwghkWN|DBH$c_h!XHvcFA!{5pqc znt$moec|WbMzL*`-mWB0RTVpqP%?-8Ck~ELsJ`e<4nU3iZ;lxOTNb{8kA2Lpw*@KO zbfQjUmY(RZa^J{6&}}q}<$RBhM%{GomZ7Q27vU4XUG5#QELRg7lPsfB3@+k_zy7kK z)0mg7+=_k=RAr;7Y_&%x+HR&+&stBRKZfqU%A1`FwTC5*4W5}caQx9H^MCa;`ZlZ2 z+q68qWvr#Izvi#OM}ux|JS#zTB%;2&k=IC9WA6NsuI&OpCML$~$LV}VH8$;?^_p~N zRP2&!omHXMK~u!~IaCM!fP>Qdb(c{M&>_E=uoLLCLKS(GfK05#3H4P`R*{#lj2J9H z5Sgxic!5FtH)ai1b(>jg)=%hh{95gIn-;bBO4HTcg^Wo3M>IcTf!b`_H>HDfI4bk9 zKU546WhrZ8#36_LbDHaQYZ|84%idcp`vVK1XuGYfafw(ryqE<_9hlrE)>pc>0GVe= zn@7M1Vd{{_1DT0z7sdF*xED}#)#-w}k*ar8R1dm~+>3-}MOK^^mkJWFlySXLnG2>* zv22g~GbbbR1E>H1?gluZ(|9CT@_OSG*P3gHvT9w4N1*la^5EDiqR>@woNu||z8Wij z4kIOG9G1XC!5PC^o1Y(GIGkXA*^Bb9OIM>-lTW%6J{d0|Czq%&l=r`Z7XS86W}4#M zolBq@QFlwE^^owEo3_gzwnW6dYjs_tpvRC=XM+C(u1^56#d8_|JMR(Ro0loxraMyh zdskxWIXd?3Sm&Wbog0>5Y+X5A3*Whr6LG{=9A-i68Dazcd4YT|foL$bp|LEO^;t_K zrx~X{T}xV38Sn7iZ1AMub36{M*`;`qDQ2465e4_{Oe|mLow?ki?>f>crMmMC(}W|g z=Dh8>ntz22*WC}c@)*Za7RtLJyBGf|cB+!X($xrgW#sv+R{lwj(x)@7PdDds zj-W5iPN3*SGPUX+|6;S%8^ySPZrkWTZVe<|y)ZN*&4lcl#N2Zt@&oc!W@g*gw!X)H zK4((>uA+i4*_ogi zY?qk(jCyBEVkucYpYVq3@N$0Vd}>FQvK!;?OXp3tZN>SxC@p`rs@`(f3qNH3QW(Mf zgvS@39D5}p|1md+F2g+o|C(;UkK%h_PE0s{dw;d1NE~QxqgPaL{9_vXDRtleLTY)e z?%W;}PtGZLHdRGK+VlI9>NHi6dR=CsuQ~5#E*+bq+NO|wKE2HqNg5yJ=e)`4_k``b z_s~Zuxs4`sSpp*Ytem{@%afF~mvtE;>^VpCVLM`|;(oQ3i|5ydc{&391q^!)oC|s+ z!JkNl)b04$f3tx~l3%(}TdEVzuRo>8Cun*@Ih{jEdqVHrw3#DUtpJJF0{>r&ExhZ1 zdZqGJ0iWh5&=io9x7dp|^wa+51jl6Jgdiuo%or(PL+n{t9Eqpzi%h?&tQV6to@|i` z9sfDGZa9w)9aO@KxHj3fs~m~OK=S+^*gbjFR@S}PP!^LUSgqI_fnTSXz!ODW#^58G zx9UZ0Ep{TgfP#tP?)I zxovBE{Jp<`$9n5m=UvcMQkD1$DjKSMx-IAa_(;zdjp1>~U&udv278=T+q_s7>pI@F z>GemqF9Nqb+knLhKsUTY5z)SEhg6qHa==U6?I$Xhx*+ZoASac6t?oxAdeZsc9=Zx5 zi_XaUKznz_5tcVUXi)s?l(3Y>w7Yi3BvphjKzHN*nZbior2sD14L@Y|i=fVf2lQ&- z<}G1MXS(PIS*Kt;R%>V`!VugHCJfr z$cthfm-7yDChLEY0*UHumv<)9cxsZ~y{^8>Ssf1kM9RhBF=Bs>u|#@xlG6Bch&f0g z_sL$T5HWT#@y+vis^!bLkxDtIbRWgM&0PaC>bH7l##)hafJLYxo5qurl%?OUFxH2V zaAA7*f%ajy=qBWI+(c_|dKlwKt{!}f+gPE_GRp8m=`WG@FM-h z1%0~C?h!>;k$82Fzuk6!ED9A+u2XjN4OZ-WSKd8po-Crm>F+ClN`ne4hV6LO8-qPb zW*?}lOlm)wMU215zIrlO%aITy6XykBsj6YmRK$MT@}IPLHoO^u$O1}r6;G?Q11mm~ zXBX0&)vg@XjBJ~Q%P>r)5CIg9Q&OQl?hVXm7MMFKO*TU0c{7+w>t(3$9)w^{RUh_zM@kx(mmaX;uJ%MfO_ zy9;k~-XQ`;2`;;`E<4gKW&4*Pu-U8(7A`j7G>-k~`3t%47W79_SVAFTzKYYq{+r1J z1BCP)TO?GfE^wE*&zLG8$QxNMr`M0x=8(|c^z>byQ~|Pj(C%Jk|59pwZBH!Ejc|2f zgzm`D;|S21(!Oo~r8Es||AA^mpUTmnwZC0Z_+%c@=YFj#>+BC~SjV%C5XN522eFFI z8}612Yjo<|bCQCoWjt=VkCHguV{Jjf$!0wmU0CQSu6W@I-TYY)`FI!yz)f@Kt(uL; z7GEaf_Gz@%J~R6*1t?9SpVcv zhyVHR{O!P1p{C;J(rb~+-76v%8;tHxt8Xt}e(RB2x4CYk?Z7Jcnt#E_@f~l!#b!1d zv6*Ub?`(dzM?#;!or!|qZ!$ZjI9;p>jLsEhEn=1djK_je$g1^vzW|kCa~is+t!-79 zBJJt_7}+g|}e#tUQiAIYl}Szg)8Wq7li+*DhF&;SKCG#@&XKtB}Aq zpA1=Hr%$DpA+tDY0Mb!C z)gaG6WvSlznPS)o>a|B$_sOD?KT9qY1%?mX>z9N-(P?b8==d#7Kb_aY_t9&_qH})5 z#L1@g_GTle_Yf4#*q>N$2ux(*Z0$x_cv-C#IrcTk&RdTOdE2NyV-PB~n8+>7 z%-L;h2#gJ-%^3(VgLVPPzzEPfB#0Jt)BQKUdxkwQ#y!xGbnT~w@($fJkh?xPR*^8KFL}SG%(&5(W?be z67W=Anq7D2Z3R6ky-Be2;yGdg{vhl&Jf#My3K^$Xrt(p4)^1)mX4)dtPK%p($!d`aR<#|@LTJpzY zWe&O&C5-Wt{5ndoJ~^p)7$JD5^hd(5u;4=G?+veM_GudSwUH9B4zhfj`7xT zf%pwU;isHnq9xpPoVg8XXlTS_WNeMA=l7EZa`;|v*O}LVtA*bjJ{(<`6m2@u^Ot{0 zPGZfl7rd%b$9rEPAwT%H{50A);rtgZnI<4W+N!PchiJZ>8Exr%J$L_Td1mn!nRNq zhs5BB@zqHzX7UHY1x%MC)H{zqX}j;s0aj8u>w`R)ai;lhXg+N<(AZ!G=^h_z$}($i zjuDd2!Hh-uWLq{I`tD~&u0jXSf4D^$_w|77(Z2gtT+{061;novyoSN(C0^|!WL~b* zwIZU(^>rv=(i5YkA~QTeW$P%cp1he6P*FLY6t%Lyr46R742K|sk?7+v6{yra9FMy^ zH~)Yg6)k1*ANifc?-cE}Pl(0Ara;EZ{GIGOL$qD2N!zQP+kV|)%4)r_bG)ryl$CSd>~HoszOlZJsT~ua^%vd-ITJiG<<=#> zm$fHwn%gY(0$DjwHY*$pQSgST{Lm~2{ge++Au8WOFaPps27T?t4Jrbm)jc!{hfB|| zvu6AU1(i}$oy#QNd1!)c|M4xMUx!f*i#A4R-*zP57+{|#Woc>IQwKyOdYcXY4DUCN zG4^d~Wk}du`#qMnyCZH2tACFvjy@{M)3C<-=qp;H_FVBR^F8Zg3~$`@IQGzC_-!}% z+9%1aZ(|Jxh{iM1u(B_^6&#b?{H;Bd@NSv(zVBSwm#+e5zUQQojZy1ipXn-MS1zXq zao4ltgZ#I=0T^?roD4}u%T(J@mj=vebI>{G$gwIJV z+=TWWwR%0DF1FUl%nRwCVw|297)5OFt45e#X{46zClM>+IL#n|2O%t1w2n(?xQt5Z zo-PlW2cI3few4phx+XrO)zLl_nARtS1!0g2R#vvB-x8p}9%H&GfSIIdF}&T< zntXi!Mw5S!Moca>q_8J2V&QZVOUCRk;LzA)I{F7mLj6)nLHQRt9n`#6r`xja$eH!| zAGgzpQ~Nzk<4P)(?pa`-?v)o6b>{07A5_(kGIm_qt4RFmv+Ydha)j=m>o25VWqjwg zm&eb&y^-X&-`F1?A2romOd{ncSDYO*#iswLoQh2=pEYdDwLENv0?L5oddtCN83Q>Y znZM{K95J5-|C_AW(!=Je=i8lBx)4C<%!ME5osuk9)KJy>g@>-~mFUIyq!Faq-kC5e z`;MjDuH*K?go@<8n;PRxGp6wki`i9Y86$LVbY6z>>2gnxrHlpQw~S{A_%@01)Q*+X zTAHap4)a{PaDSeedyD5=!xJjiw3*igjcSF3(Aq?c_A%0MTXfC-h9EaIvC>iE z^nTOWqrOEzznkm>(>O1gUGKMtu$=GyB&%Llc_7nl!2!;*$Vq6F2iIrXCvXk!g7cF%W23>1ya^S1!c zf9(4GjqO=Va$snP+i17)k64sRXzeJe%RP^KkDWO%C9gZMk@oLq<%2w_s&ZL5E2O$l zW*UnRCyRdZD=aVm{ktKFCY6=Z<|4|9M(3}p{2`N(qJqLle#1P>_NzJ}pod<^_v(iV#LS=pb5F#l)nDW+0-Ttn~&d ztg5yE!U-+!Yt2ZDVvv8l?H`f3J~gAem<;qAEVy0IoUPl8siR-4ozw@{ z_u@}h7v}S450uCOzo7{+lKU#C6yo3B1Z@~HwEcxrc0+zrbHaP_@e{Wsf;v7bYWd+c zy*t&H$MGigh=)scG|E3k9qFr+&s#B4`@%BcD_ZZCMCZv!NNfQcsDXlDVdOjr|4=bJ zSK+l`WQn--{tvsmyGY9!o_F8B3;bR9V9qJr!xr{!zL%%zV{zOE+Q&;GSBL&tSPAS7 z$9_%so=ALBtv__J;?CPZ+nYrVUFO%PZ%|yw5^R_k5Q)1yG||HN^(%IvH(4>jEiF;M z-SW1t2ko1Ro++214`FNj#d^#w9D`;r2w&Z@On3Vq5bKaSw9&%z)>VJE_E9*l zf5-lH_OX{e4#g60iZUvsOpQ+M4~4k1TS3p)CpZ@~^N;Mxt-j-5GDulyN%U?s6*Y$j zlU1Y5&V3QV030A!3_r5CSgf{m|CTTD`U!uPc`|`j(}Hgkn+6~f42vMiNlKbIcmkX= z=M`HqlPnaRn*s7;;DT`0e11M_M)=8f){yICdGTg>f>F&2uBSDWOmiQL93+<6xIleBfT7CyEkqbtl1T;jxr6*cqo*e|yI8+Xr1$A@XbEByX zjrLqDw5rW#WP#lh>n$FiU~&AIXyNlxN1%ZVMQD~8V73G8@n6-q(m0GU(~;P}2&4ss zKza`zr=-k@l9G~Nn##AGnmHT_gGNR&G+%Tj1Iet_u+FX*+_V8Sq|RbB1o0Q3@*SO& zM1FqbG&Pl~@y~O(a#o#cv8mF1#Vcr~R8b{~rDbne${h`LvBR4iF%wbpZ$ zm&v5R*NBnh-lA7gTue$z`UwOAm>62EZdDa-G+M9pUaccIE#`yJ5P%nqva)hie7ry4 zW}TCha~z7E{xfeBUCWmx9pyl2-too+#+LKw4)L^+ZO8fGNUHs6M zYNb1SoQtjIWd6_hG3nFlDs{irx33=;+8kj3Y;97%Pjp32FevD`_^c-A0f(4_!~YV6 z^lEgTYO(SSDgYiocoJ?ZUInBY9Tg=G5Xk|qkXo6_bt5wE7&Ow*Bc{h7J_^e8w-Qid zp6?oZLf`$OlRys9mA@`Vn)e&z&EV*6s&$(#K+^+uh*j&Y3MCcQ(QYOlZo^R)ynR0Qs(6};6t zZ%nuKji2gvij%92@U`~Q}@D1)Qio(o-8${Z}){U`NNc;g&Hy7QgyA; z9dT&+(FJ9Z1<=#O`Vw@DUfhPzVY2Ee*B9nuslriE6cv6&ERGx+V)XFB=UoldzE7Ra zdZdTt(^^?b|LWmJ1JJ*Vi;FXR91Tpz6FHZkx1z=HN$B`8Q>~hUF`0~PE;c$R>i)W@ z0G(NS<1w6k=?p*zR)1%8U3y>q3kI^peT(<*Pcgz^*_2QYlEbGNH?yi`eFYFCNJ$|urRK*yj3zwA%nS9OJhE+fwtD1+d+Wr=+TG0(q zh{IpOW8+-~{|TyzMz%ISy<_`c`DW`zhOnQeTQ9%uBsSU0-`q-Y@&@NUme$c6j_?N6|!mu-_Ah4!Ewl9Bj85!z4yWN){HD zQ_6Ngc?*@+f=?8blVczL|G zZC=%|?M3_R)ec;#z5vm6jAI|??>PXVy9GdzouQ-pm@w*TuEq?>LQl`=*HHoE;e}h#PCM5zUH_gl+Ak$3ySY>2 zHMetTK8CflQ@5YtUOw7i;`H~W6@CdyTlz^H<#Z{<7n8-SsPXfyop^kOWJFK-it@_6 zIV|q3SCQ}Yd)s;W`Aio*$)`8E33}Mw{7j$1vqtcBG=hCd9 zTdKM`Xzp*z@otVapKW!)(DfWT*ex0+GI%~Ythqj0y4{_0;ySMlp4PudH=i|Ox3ANA zi=+}HVS$`fh+_^&+5LrO(E#GuF`L@kn-ky2x1S%+LpFxB2?~qLH}j#iEa!ROZ~Hm2 zUy~TWN7B5W??Z$u+wv{$n{F{3ubhD92x|zcM391@2WVhue78|VZd&>pba=o?j z(|2RrqoM*)3vB?Mh51TL%*U4rpk-T0!349jw7{UAzrX)qmrez}2A2_SSKZywG-hgS zu`mwpvZ=hPm;N#h?k6vcV&4Y#c;IXRlTWjt*-0X;KlVpKdAa>3(0~a0UrqjvlgES)|C=nbqyq&h{s?7~$7R9-x< zhs}wrbl~BoaX8*GS+8-Q0nWmWnpTO+%T=%GiP}3p?(bocK9sO$P1o>aFT0M}r&L-V zz$D5ux-x-0(#v6aGlu)4^)V+U9ruv${R_tpj`vl0Gp6fVBNE_abB|Yp5;}x`)|84u z`z~YhT%DM)v*v!@8~Lle@a1x>nd3Oo(G+{##gNu{-P!w~k!D#Acxx`}=M)z63m5L{ z+)3QtP<|&wzm5)mk?^pW^>X+>^iY;QrX&F-=`Kj9G|D0ZY!Jn|8*He`LK_G{U%h%Y zDzJc=vcM42`tL#ERY_n0vo|-;A?Qs67f*21ZA~Xslf*i(r?J1rt)nvIgNX92*bQn- zr17UyZz1z=GVv*xZ%&gZHlJ{2s2V|a^)Kz_9Q!HFTn;UNop}InJii372%Nzy!{NVU zv>b;!u|i;g$Rb}qeONA|i7JRCYw@!m{dOBh0A`QV*-9for1fPgklBulm1VU?>&028 z)ox4k$M+WW(&D3SK1np{SowJ{so0na;M!KNuje0Kwt~qTF1lb`U#j{M1us&8a-VI@ zg~;Ie{<-n3={O?VMIpSnm>mZmII2L7f-yRnr~f`wQu5aE+1Kp@<+{~c@3`oyZ+A+N zE(VbM0fe2ep^%Bgv!|JZLnZnJeh}rA$!-u3Mi5|-K8Gx-YqZF3m<+ciF(>&_;vl0>2gXCUB;MtQWV0nF`dsFZ79 z*6spx9B+loFg|(tI8v@ib3@%~l%bOdrzCx#kg|NRl7cUx^#LYb*7h4U{o9VjipSEO zD2cz=XwlrAv|l7JA8;j*tdDkpY1ur_;XA;pgM$v3Mx2Rz|2vqWR&OvV2;^Fr(ELRK zU<#~na)$4~RgT42Dl?vm-=;kpq_D>vkPh%ElhcJPELPRK;)mNeMlkoc4*54Y5{G+> z3NnWLKBKstRP_sjV|UX(+*Wvx0(POjQ+ZN=U{%@D`j*^RdbbmHW|?li+MFURFm~6e z!I$&HAAOQCxoqNyBG^pKNils3gI#8zs zm{sLK=Ckp<-Y~7a6*MJj*BEPe5FzmR@!K4qTWDV0R>W+y@FzEX8rstBxL6!(HSX49ho3>sR|X) zE(W-El$4Yv9?zI9e5nCnpwdnzxqwcq6JKY;>>b{)cR~^R9~yk;w``#>ONM zi!1i{@g!bkK{KyMIUaGFvUjt*y92C7F@V zwB9AV(gm@Bd~96s!A2@~@b6|u=QE8D`Er@GRNELz3f~9+c1kjqeq^{lnSLm5J>%RP zO`YFsd=(gnnSSi@5L&@()k8|4t)0z4_c0~N{(n?`Wl&vBux%1ZAh^4`ySo$IA-Dy1 zcMa|YcXxM}pa~8Kw}ZR8^LD;>Z@qd|oF7m{)!8$9db(GyUh9xVdeIrVUuX{9ncP}B zeERK|7Cj9|g+$a`;97AoWRw9Ne2#6{EfWgear?!H-~ssbWz{$!S8P=6*X-XWI7*o zTy~Qrt7M5MBd1;=iQKoN|11*N^}Hg8iw_>OPeh={8%?jAzi{S{(R=8zVX!&?{+`jx zfRi9z!yQl&3~ky6bs7Mp#z-QH04K8eb}umAjW~#iKM#{510HZ(%BPy0pU?POx`mMNN15T!n^YDcH9s=ivbo&`A^rcl+@AhOEnVzO`_2 zBau#l04PS#kgl4gGW6bREhMQ zQZ$#Uw!^?JD680F)${s%$|Vc%NTeZQ7z5a)OhL%M;>UbADRuM9;sHZ{+6n3x8%3`Bbude))$2J|$& zThaM=i@HjSOmHW2c% zEuRdfvw-_7uR-hQMhRqG3IcDb|1Mx4T74k`ZnJ;CcXoDioQ^^li?Dy-e`uaOaBo>e zf*#DwwP`G5f)-gv_k~au6`q544i?IUWgQBp*1>vk`6SF6?RpPZQxk@LX@D%w9ayB} zg198sp>lsMz7H;@DYew4_{HHiS4C5TV8?@RX`u$X{NcQIkZ5IU=fKXaUL~xp+U9dY zUA<*5?tD)O=%Qi}>->AcW_#WS$$>d0)zQ3i>9yBFXhrlO!_+jdzSgI5T5P|*uODmt%mA44}`!)-AScBc)0Ot$-U4_x`F=+kLhd7OSaj>2Dwl0lZ+v z)zrQq63mvXG-`cz{PXF4-HQ>C{{}Ejk!lgW_6Xzc3=P`KDpWt|@AjjO0MpX$Q5jyg zOK~;+3=-uvh~)N1Etj^@BIH|wxcC8IxH=7?AElz6T&T@rHjx?UPJD=SbG#7CY&>)U zRJRaJjswt+PEIT9!|+$7jj+f_;;Q0}BiEnCWJ!Jbt=q#;ICKXTr!xKs z%@8DB5DVL{dl=}oi>rtdteCze2XMviewmz0@Ksra9$ zQmVup2j}}Q*wsEOt{Gh3#B<*qrwMr)(E(?Y*YZ5{Ur>VcoCHKg3K(=gn8q#lzLEu@ zWD1H*X7ev~xH0|{R#ON1g40vCfoz#*H&0Z!#;aXw@NZKwoz(}pG}Y&}ZDW{Q}u3T*b=#kkjVNpX01 z2xNAE)z)yncqA1KjS--?7)`@Q=yQWylIaNZ{tWKuykGC&dm9ks2Ihy=)YKkceLZiF z24|^Nw5auf(FI%1@2*MlS{MA6gS_F4%fSuK`rp+rTm|6CvsQ$e;ZDRmYB5g(n{uX5 zF9wQ~0Uh#k0uR;V-7WbOYFcRkQ0ECTv_ExOxCHhCR3M4?0fSEG&n}?x6Uo@=Uu=fJO@-bx0Jn0Z0Y@lS>4~2Z3QUkb8-S(!*Yc9pDoT;oh{WqqxixB}0qn?Crd*i~e|C^i~|IseAhaT3aPxq)H2FuuH7mxe;ADCQEzB{ko zlp=qO%|RWCh7HFwlgq$x1yj*`>ScOOaIZR!(&vrg%K@;fy(|R7$|-YViU!k>9Vhm={EW2Q95WoM zCC=A?wRz^XUb&rEU4eZbzrx|{yByAj%vkT57Z!V0s1i;}v~6^J5?MG5klInw=0yTZ z6DY(wkjB7|!B_@o?7zw!aH_3Z&N0N=%P{&JSjP}0f5F5QB=i;T4!!_=Nx~VzuL}#a z+)&=foB=}0>+%u4vt7PQVK6`K?5Lf1ETH>CkD=o6+_)crMS*+wIrH~;sH@6N#_vd^ zY1T4%_;`(0+*TqM8(X5reo$gdxojs{wlG?rN21eeID+IjgOmMUkM&t?S6d6fJu0do zQAy@t3DD!8jVKE<`~5Y)q2pU+AW6R3LKew|ATKKVFfcsGY;yF&X*sEM#mosf2u`oJ z3)hRrvFuiE*XO>N$Riu(te!XK0Pgx^0L$&&?9L1b9s=XZA@lqD``5cyyC>^i9dmu+_KHX^6_60{ zu0yi{LY1)O8xr7rcjPk|l zK=znxt<=l&#Nf5FpwHF*C=mFb(s|3n;iix6O zJdF+RGCCl`Ulh4|gC{!$i$zta-nEmv^st2pdr!-6MY{HBT~HQokKq3eXyl_|fijx) z0+F0ET7c0{GM?l$P7s~V)7~H>K6|;{;ft4=uPR=rbI%RpK|m|=FK)PpB%gj zsCQdM)d+w$T@(qleEXE`)~N1#Tw`q9ez5CKtcgVh(e!$# ziJPhap~V$Gp;qikIIsh zNG#T3)l8Vf1?ec_2On$@M(RTqXZE>h>Y+H1Sb#=+VFMEsH%Z)@&}U@0#UNOF^7 z^=#s@7mM2n5~#_X%ssXS!iOa~p>>4zROwi2qLGmpt(iIWvY$y>zz&pzz7I-4z4pI9 zH?;1b2^LsN3-Pe=Gol46ZcHS9ll``_+veoqai;TVaQp)S1D}kV_$%PjHUO;Li}2bO zQsFq_=>!_%@|JXw-Fw)h*O0?Cf64D(p&mB{_;6iZJyM1TK-b%O1=b3<&48>>Vcqao z!Rj?@7=JDP{(Mb1_=;UiF1Qj^YS_26lfCf#DF(O~gM;AS!=z?+VuVW5D~bLp?E?uy zVwrLWb@6^Mw(R;?%#_r1I|Kw{VzRN`3AiOzoN=M39kGbo=7O8Wr7XX9o!@Ikr?JoH z`TNU==&k45UQANlixC3W(SpS0n~#maIv^(pT%;u#7*y#}p**i+tjpe=Km+pPa1?PK zz7##!L>>O~o1oV|K9*Y2>*b^uXa<*BNaR!AeavEAt-(T*LAaIXik7-AZo8>Qbag~~ zvYNB*F^+XlQ*M5)KiXia!B0g|8Gl8H!?dwZH$T6z3@Oq$Ar|V)*qHFN`@Jr&v#k5G z$8M*|ypioH(4cF^M5i@=|IJr+?Uo(RXZM~AuJ|Sy0scb6tB!C~?H{et05}9>(~Xb! zQlO^Z`>!g7^N!R>Wzex<8%t(ye|xeYOWv)Fq6K=~p($W=EzFzKC()LZ!!E*10fF~g zaPP;Im_=Mvve}QWe2Js3G7*?^oH2jTnmo9mGQ}#3f9D2prBs@<9EmEE+IV?MxR#)^ z)z{a%1NX!(E=A+p_*e0R=BA*Uably>`^C}waHSx1be6?dG*-4>SK0m4$DDdWaKxoQ ze*6e?pO#X3TSepGXDU&buJh3*&glUh=xsw>D97hloxBY57_UUqO0}f!aSDk1t_>SE zf=$mI+=s0MKm>k#rNgv;CJ~P=vr{?by1#u$50Pv6jN+R9F*Ly+sW`9p?e;v7QCX_u zuZ_X0DwS-%^-Mi-o{N;66s+{DIR9mSTmCmt0WcZc4@@luxaXCYmiD1o9BA{G5hJkNTC#1t`kpd(2G^KaS?ZR|!;n%~sM!w+5Y2I(DN{~l5 zKnin_=`dgdG7@4E;-9k*Eqdz-d#)AOtHL11NB-_9T$DZLv(qVz?FU)RPx^g_l|(Bd zFQQM!lt)^wat*Mv_|~){2MF7#Z^Tb#%(AhmB`h%2CXVPUiM6mr`iGWGp{aJPwTg&p zF_1`ek*QbzH4)1BNbg3^D})r>wOMQVI1w8gJA7=@CME#zj*NklK`w0s1p^}xusUuk zuSan+$I2s_`YJNI76r%XpsgO4*b;Pbdc5<6q!ISSM0|iQYY^I031i`B)@zKR#-viJ zD|HVIvjE*zeDdISeH0jjh!HAPMJK%)cKhIML?Nfg z-XA<(mn7ceDqLQuERlfSmbyQ;OioM)YzUmz(L_wKeSyawT4n6nSkU(^1_TVn#3i+n z(dT7V9`WdQuUn7CBh+-wMz30BivY@oNq-1D6&eH-BKp6F4eb{eF+u^P2>vw#Qd7-! zsuibHg>s?2fliJ;JieA&Qijj-UEYdyVd32X8yvH}wjSj(;t+PF2`T(-cycU2mk+mK zHC}$o{5{BDe66~-fW(nX&3P|iq!9%i6Tog$_A%sZXq2gY*vhZt$a*Rr+AqjFXzd+= zrMuVRS^_!WX>p9*Ca1AnORyxl=zznR%48Ubm5*xFvXaL32w0RJkogWi+&q8UR1K)cl%2@zpZitsPwLtnwGJjNjs16t^sDi z_aSGA$Abk@*xXB`rHcG+SOI$FA#$f4iHDp+0bS}iIj+(6Yf<=NBL1t_h*$K%MtK(z zbRbcqzHE51c}f%>56gv@m^-8ezw-U_`o_nfM<)yP)b5CWC$)k~#)2j)yfo!O;Psq| zXW9{^P0>`xZE?6r9u~?Xz#&&mLSlqdg^MWN0x7#CanYo~-bSDC3vvt=iJL8v<6c55 zs`3)&kuwllNO#o%L#ebh>LTUO!bp&LgFYTd(CAQMo#vim;$AV`Ojo9twr^Z#?gwP4 z?~n@PT9X?xPk8YjjQ`#Mil*XViCT|UvdT$flUory6LBM?&9K2H+MGgeM&$SL4m3pm zv@NBCL8`uwfB*bJx)ebIRs&hRO7CB55vs~PkBE#cDBzZan1}UF%jP^N^+Yct)Tvi$ zz!C=zC(P-umTQFo89VEP(L%K@zJQgd@$)zJM^Y~@uSbD=-wer{$%eW%+MO6;XpH77 zpMhQPZ#VUt0|;ro-AX0f(Ge-f;nse=Gz9p0>em;yw)Toc9(M=z;#eGRFp1L<|q2g6ywU3E)AIg49bYE297y^aFqj zBO@bAR??ril$cMo)e9d41qW}qcKD#knvkDOF^Sx^Yj^RJKKtj)Xbwb=jmbXV!E%k> z|EhnHLE-df%nv5QOHA*`H^mXF){zCS2I^zpWd32fkIu~TLawgOSisH+b6NZ9Jduif zxv`p0#r1*#u#SqF`XdnyO?ixaU6noU84V{Of_Sfy~)xgQh{gt`8LmUPWp~|a6PHQ{{Bqz1vyEaw>s|2}^v`ttyVBo}~m7y(NGxmgQnu>Rh@ zOu}b}`w8}aj=nu!XmHw>2l|_TpKgxk3$x6>OJ>=B6BOkC-Ll^xY^dp|g5)LPCCX^vW zWAp>(4?58P!MAdJ2oP!f2d7ZcT#)Y+$uudAym*fDTxt4(|_F!#f z35)Ddk9|;{Mlwo=HO8Ufr0~6Zq%dS&(nTcJ*No)4f=Tyg1OfDKNdNPdzH?>C$MD~M z7kIr<`4A)c=8lIj9B%v5rNKe?i9ce3U3sz=|Nvl&piJ-E=EYG`N(kBT~}-Dh0kwDHPlN#9v* zb4HKW!unq>fNs-=j4+HqBsOO|_BC}z5{rIB>tjC>ZK#|m3%9?wN6?D`tgNhxG)<(c zwuJ z(h*5}nGU17P`kp$Cbzv=qPJ0*Fm!eLiyY5ujG4tqJFK#ju2?REBIKZwN<8I7|L_qc zo4M;XNDw%sP9ougPCdFGmzvTKY~#|BnKy6MAM!>tY;5e0mm=n)&j!pM+=8_vJwer| z6UN@Z81^ZlMM858MlqUg*8rVSh)0J*M7r;_xo5ki7dcuz<~ViCby4`NG34M~z+_$4 z(lT~}UXRVw$L1nMG_u?M^mc*!e5T?sD^p0VX#vw_liA)2{w}$d`D2Hi=z@1&aJ7Y) zq`2eb9OuOouASr9v}Az=%OPqI#O88$0I`!kspEnKa|lr*YG%@4;eMy&Ry(;h!L^q( zif6bIWFGtF-sNDAzM+yDiCGL+%B5VTd1blGqGl_m_5$0M?&q`G`yX^h1kXG>qd!BP9@me< zJ$jUM1M=CwkVwq#L`sa+ zn|HG}GRW3FplIEh!RrcnLN3`HS{NxXw%7tVIzZc%s;2bEcsjdFGGw{L-XpPLo~pX~ zr;SVJw}BVeMg3sp(Qowj{s3$`UE&5#-z6dVNfR4hrfQJ(&1Slf(WvnLOnmcBWJJ8v zoEd084?<_{e%g0Hy&g>&@O?*aAEpP+`CW{%^U@2f!+TeT52^3)dcj1$bL#*@RuIsz zWfrkN4_wS7ZavYWzwC1{pW8_cw}^OPgcey*R#OuX$4bq;Io62+)4H5$_n zxyn&aB-WlDB~K=44GHZrbED4C+I7^b7Z&VG$x*iz=!a|;zuYY zEp6miREjX-z!3QwSi;?Sr*;)e`M!oIa(LZO$vw|pHH>3s8V;XBNF!`thn*&NQyfKN zwLaz7Wn;JKkHPf{T+qDT_F4>_-~ZLa_P7S`cTM(~xfp(>p^NV>c@6iPB=iTZ%A8fY zxxZJK?3|WSu+PL`j!!^~n5JhMNHJnHu(->RgK=TN2WQvQ6GYH3YiLNC) z-K?F7BBvm)^I74*v#@Z74_+%SSU{n-bkzOc7mV%^-@}|UP)K&d;njhq*x`D5bfF2t zU6HIGaf>?55?}e2`V5n&z3>NJ4~d8CZieL7UZ>arqtgASD}v*&!!Hxie_M9>&_ zbZ<9K`|DjCRlbfb%?;C`mhi!k{~mg7Zf@1(`4lT;`an5;8MrX6tih;8V$FZ+;{pBx zGA5>^mX?GD~LQFS`M_`HOi<+0nWHNPij!D@sFDM(&SO6n^x{TsfOYiM*Olh;Ogp1 z-j34CMmYaa34{I9(C-t^TY&rQ@QAMCJJ$qQn#@9DLLo7)s;RwrJ@vTX5XK?_nUm_z zPJYr35DX5G+x{E2nacx$XiUUPC@M!Iq-{8Ogb%t86ER+rqk0B7%ge zX>p=7{8>w}-UKWzag2l*loO(04{Mf&X^laP_8gyYgPM|B5mDoz%`@RBBpuCe3K#cDP z7PJT@2?a%nX3nI@f(xMS+*=%-fzS>IkQ`?kG!)cp^r9`&qi}gS4)*yScm6aZF(Qm@ z7Bb^-y3zsLJlcN`9^6*wG1zU{@Fy8sb6XvGD2M@MWmMGFC7hibqYf~Du3sb|&Y}x9 z{c);m4hYA(M#&Q*Dp$_pNHLI^9k2(NXAp^SX}xEttYmp+P=Mp3=s@JgIyvrN4<&%A zI#OhJ{X#iP4SPOZC__0d=!~l0{f1rfA|On(AGMY1rfs6NV*aaK0x1Z?WcCko=lfd+ z)kY=jWWJ0}3Pm_3F!ksyaLkGpaa_|yaj@S-!kLnqilzK3;U{n}az5qHH@y(?%K2@2 zP{^l`b-AsN2^RCqh0zw~h2;smG-dGGy|Y$0YQ$x)N4~sx17se?RjxAF0tWBQ{?Tq? zX#AfCS;tb79w|stf2g4)2}Z~)AH7958O;{T{DNw$jZlTnY0_iHzm&4vos@|o4|Sie zlqnjVyO8wcc=OdsptZ4D4#19MOQh=l1X!`wWn$!^KMnoI=MCm9WX_}zS2!!_U(+7J zX9iQ@Qd}3xN`^=?4UC5|;&hC!!8JYL@i^mxrM3rLbq{o{?+Tu7EvNf0B( z570OHu5TKEHXEIu-?kbFX~7X4;pO`*-L7f_!AnXan$wIFrNi6#Bqaw2u(_vY)kDxm z0nz>6byjXxF@dl@_O|6z-Lt8XX+QhT_UYP2Hr*TGgjy_G^E_s7Ue0AM@jc%pz~OPl z9UM^on?v}Q4^7ip@2fa87s`WPrB|NQ&><{Sa3Lh!t{Av^^2`};nQh-V>Y1e5$ z%fr4X+MvG`_OiY3M=KE?S{E&~?y33l$X}0520d5)ezh)7_53VRd2&Ny-SjQ6Cm)3;olo=%{x;vV$;uR z5H;IH9WmRTDgtJ6CpO;CtMo575vU5{8`-;|!;Aj3r+G7$Ex2E`<=UGKDkg~jZAUiadAo@I-Oi1-i| z8M$3r5Cg~$zsc*Zd%B)13IpBnG>%h3XSwykgJUz5t{n~$->#hUO~jUPf1r6cwb_sh zGzMJ5=nf>e?lwJSq%xmjnn#q|9AN{Ia!2>{!>v6HfQa1YTOOpAYvJcVSPn8Yt2u8_pOVH&Tw3c&YJpDeIt)6Cf)i)3y)II ziFHNKBlI&ZEH!mVD+5C37d9&Hd~v5)ctm?ZL*>DI+L7a&p@@VmnLvprNefH#J?`&p zs`Yx#Gc0Z-Ul0ZSkFi3T){~ocE?jOwwuRsKs6aMV(z-UcX#G19$x3}kT+}DuBS{kA z>6&G^P$W<)POLoDX|mk+{qR@WUcy!^?L;XsvwT9=>FYg48cnQhV~4?68o>@ZoV}GEI!(; z3zydYtZD|_f`cwDHDs8h!~}f&aGU!aPl9(3GEz3yoC&9g`DKDN1E0>+74@*0MTSMG z+pG2d#Boo~qA~y_3_?_cIWG12TQot>c(SzEzq*j9ks9dy0d@pXr|!{VnB65kHc2c@ zd@Sj_*>9P_R@)EmdkogWyNfWR)7Ra<$GTElQF97rbOY}*cufY_elA-%26H&_(cCe+ zpTXW}Jt3!Cgb4#L07tsTud5a7FXLXw8sc?Wh4^s*9P=N55W@wKRF>LZtfpd02$dZt zh%{b2L+d4t`vmESt`7(5VH6ZmL6=dC(HFD8D3RvDLmG!D#s%S26uIRAZ5+RTw7 zwUo>Tu&tne3d?7n5T7Ti%Z%i3=H=9=C^+W-YF#lytS`l;%IZoOjm88-hGueSkw0tt z?DV!KictuV2f-ziTW`1%eb=Yz)qEUq<2JoR=ifWErfp8pg;{5-bvt;x=WmAY;w!JJ zI=aTUix*}PzkPrIsj~|_*iuGoNklieQ+%fTldHl51nZxb5R z>S`WvU=LqWk$X%2q^#oESmf?t5ZF{mM#2&y`Syx^uj9s%{rORci6PO=uabhrgKS%& zyGuOA;n3u2K+GIpM8<8}Z__I1j26upfOss$s=f>TnP*SLdxoMhZVw06_aEfM@-J8I z(gR?a_G%SCzOec@C^S?>OAAKm`**ClIfB(Qnr1ZH!ChxVciH^D0%`_&3P0E?27|>{ zk%6}Y+H_QR-@kSTyF*h z0_N9Dc1LsoMhN#=tMMa1i}{Gf@5#2(j9zbcZrexNA0B-q^1IqQlL7OqL%8*t9unT= zmO-iMTNgVtV(iAC+uq+lJa8;5EZ^&Ygk$9=O(#l?Tro;%3P2^I{DcN-cWVO9SBxHE z!}skaz9T$U*zlLWfWIRnMtwnFjlziX68p9<4Y2gUI*kzPR_t~W_y;qeur&DnC&I{Eb0fY9tgZXbC<(Mk$8R;IvACMI!4MzJ+J;H@w(ORfyR@`4VCt-O+Gp8VEioo+ zc$?U64F?Z_*LWyKbirovghLTV4&=jrm8efSrC92T3|~cVU=A;a!B%NZo8zk_15zvo zywYL-4tPjrCi%|Z+t3DaKeHU_hnj`aW*hC=r4vBl7_w&g=kV#>X|)BUz!Xo2$Q+lt zbW^!(!Q7Lap)zQM=lx@^Dop6wMDK6Wzq)U@!NJ;6hw&l(n{lqvyu8-D+K~pZi1_GT z+`0_YnD`k01jH;NqYnqq*wy0~D zeW_I-R^asuP+UR)?yzfz184St*^@(SCYKJw2?7*CNyFZ*2u9I?f9}SRjasLMFOe=&R`jo zv`)jKLqw!UdHvH}1V$V`G^JBv;wOGN!12{k{WzCxIpcY2v@-vxQ?I5`WqB!Yg}RX* zJ~{f^O)wGL<*16TaL?x&lrE>Pe?i(+>eS0~rkyTertluXr2l1Qb(`vIh2Qp}eE&SIvX9b~7Y;>6Oz%O!;*=lFB{$Dh_k06N^7h zV*~t|Zp!N0`R-O)+VHTb!ewA2v6`a1Fqt((g2=+cU{{Kky`h+%U191g9ro0x*Nu0- zV39N!cyTc?G_|CpSsyf-(?nO-0aut)=}z;^bX&|IQhdov`H(S>YAINyOP1s*+VNXZdHrY@&kj;E{2n3DQTX%f%e#ON-2uR=cMf5b_*?9#0Cvxrd6 zwG1A9j;%`UQ|<1dG6}5DzR}Xtd2PBWE@P52d9|JA(rCx9zXO~e)qaRWj5Fq%Q>io^ ziHlPQ7gHx{UIR@+S6P#%-zibJd>QYe;J!I zzAWgXt41^5Uz66ptErs8oP8@;a3SOjld-a-JC{i=M<%Eq62q#>=ckx|xx@~W94KN- z(Y!dO46Xgl>Bh1M=^~uix2+hxf|p9JAyWvxNLIfQwwsS2-S#VcG4cf*T#n^Wm_{in z-l8o0jw})FNK7q=Lt*@H&6b91?X~v2nuulqTrk}KDCc~dt-qMO#$x=$qxgSWoU<-srSAPZn{1Ab0gkBweTT*Oi3D{{ zlmaz8(&OvbiFiMTs;N&E!rl{^dfSC_)l%-3Tv7H z9vIoIM#y3Bs`DYM@CG%Xw|f#x$p|7q2EqgqIsahmmsw!cGZ;zZzK!*qKLiro%kSP)&of8CcfR+^sOnuIR%0pl z)v%gf^w`bU4Hs-jolG#NtPTW2%wIXazUJ=Xy=+BZEv@*;z8hhiq|c*T`HA|FL|e>9 z`*Ih$DvvBbvZl3dXmDVt1<;1fI_x{WCjPGUdz3d_tlr2IjG)C7PPInY#HCCYLJ@@Px4TNM^e|fE<)Wl7h_mUHhAAfh;YR)_=`(SS) z{+4~!LQc6QCGLVP={VD;ErW`CvsVYJY_B-WQ=(_RWZHFe1bq(hZxjF+$Dr0VlYu%`niJ^{2C@s|WI1GA>L( zb5Vcn9Q8Pz*w@^0-Do`K>g!@YOAQns%B zt*$@Z&F@*b>h3XCm$dUCTiq&ot%+wMyC+1{coZ&uU7pi}P+%N(RE7I}L`yO;4!asS$q4bEYbg_}!1RV6EOF7Q1C8XLi(Y?M+oZD4jA zc$Xic%8y@fM4bZ<^n8NQsi!a+t)0gohPmv!89`TeFHu+^ph z7qCFTQd535cr#ilw8HcwlYFtD_`jLRcR1vz=lWf5Dq663?kaKmF4G*}uFb|6M5--K zMFRKp+cQ@|hQw{QzWe!(VdG9qd%0*71~>Oxm8+oRm%oU|eFLFO z|G5<<>D-Ha?k#7PIsrtHP>V&tg;zr|>f)+v`k)|_Rs8QuPW=JqG6W_$XgQ)TD1#jp zWsKoe2c?&b!PrdiH6<|%iWSUp;+pemI6XBnhqjLnl1^_y=k9ku2cvpC$T9@8BcC6R zxvF8=PpenQR{eT@yM4vH+K2suTqFxuaJzz?o++{#BWGRB_RSvK`t497f_iUXznQhn zN~3Yh8z!QInPh1M->`D~XQkJpU<;;KpW;NjH_WiJ(+3JKjj^pPg=8f~t!M(?I1o#* zy^h=d8@S_yBgu zYz0}_^tjaa#UM^`J9oWx6loe!DStp2E$_08?RcpZC3PBy=eIfCTQH|9qV42ng9>wSsy{XIG_v;t$1#J{u~G)&7PwYFnEVIrxV0? zHTNHk+jobmrkWS6EPKxdpSJAN1YX4nayX(JCI$YHMnUYvG5U_(CgwfsfNVuQ*T?|X z(s&uFyun2rF<=bhwCZ&}n~S^MpxHnLNRusF)mYe*BF8Xo=6e(VB@~Eoy2toeHA=!E zi+_fDyyAA%aTWw9a?D{g_7&EL7gSZ$l@S0(FHr3tp+7!rCZtWj zkS&`qnyBE5e%kK#n%7zn^t=&N9>F!Om=t|dW4F0k60i7sy*aE%#UBi^eP%^Xco~)8 zxW3%BQ#46Wy+f1leiIl%+o@;U*IxJbCU|9dK}~^giZMz}mC#ixqSz#EI+2yLMLd?% zU;YqWc6cyLcb%sfF}&BPf(=u|2KJb9qo^3Au7M$@W69ba4!-Dn*@ctQ?JJ)}ZQ~_Q z6emVcOv3KITU1VSUuP=rYW($aL03c}V@_T)54uP@$Gw?8r+>eP%xUxDJNOZ*AWI8z zCSmmN()S?k=WzY3{zplvL3cc_K89EkqX5=-IoV-Nf`)E5lPZMn+jtNX;CqT~E= zrR98Mzs78PN=>L}=)&lPdM9m|!0;sqAhBP;0iJV#wsFCc82{!4>04H#K!=IQj%2>n z(yj?FzXk12R)~QsW;2Ym(H)BVoPS^*_G}O}i|h;XGJ1P4)UP9IP|8BzthXB6%S4rF z753$0%k3%=2(rLm6I6UvRG#0-k{Dp7JIYS!3|#HxSFCag{;o<*Q5pmfG2L$9fp8^? z#b0UOE4s30ayV<9V=&&6_SuQ{5eiXf( zX%9iitKPzpXHA4sKWI%4R@>T5Pd<&$%@|7(Z<5`%1cXRcmNy9(UQc!z2>SvsKhUwEfWP5yXvJ zf%JR#JWSEyd{@4HYS~N`Fj*99x1?va)lT>|qugu<4p{Rwz+7X4msOc&r;Z0&>>M2g z8tUqgd+>R`wQVhHXcgIjU}B(+h|iM<2;p4~;nrOHoxc;_ulmCJy=^CPS?6{V=(}g3 zznLYU7ujhsR=d?!TQ$pHYtwy}QCfd;Mhf+4^>RR)6n2=+b!~V(*<*UnNP)$`)(SwN$Pg844ke zda!i1R;;Sx7OT1V<2dwB2j>+$Z+SQPpAv{ibu~K?mVUE&*8wUs>;NewNZt;ZFDD#V!PNQ&!{*q!|&36zcIRE_-;s!5X z!C<(;@Y7wAT+I~2dZm{C?-3=C@B zV@XJ<2X7=?9CKILnbYTV+%98-S{Un`dbW%;I4Qt5k*1b~G5F!fVAiyKInx<(`B66( ztE`?L%MiP^kO~byPoGwQkDjO>BVi0&V=yE9c|oD(F6$lU?s)QQQkpK%$ay>zIO*ku zm8-o`Q^1RK9J_L`+5P5oZrn~_^zP|~dEzHao0B_(vfa$i49^I3Daj}(2<$tmv|9{X zco(~kDW4-`zMaO;P%LZ*26}%0cxIS7ms?YxBYsBWE693fHcSYF!ZNh_qsusbzuQJk zOin?;#I_aJrOJV^6*oxj;JBWe?Klwh-fjS`n913;r z*@BaBh8JgrgX=NYuHgx4sq{a`s43(zLD9WUce<#8oAzlJ&rSd6A70F+e~T>oh)31< z%7FWo?|1Y38m;*FvR4sP6Aly-h%i~-ZrwIr{R{@RK)p{MJiB55PS#IlHRINftYET7 zS~I5V5wwGwwuB#99l?Ab+mRWy_8fDsZhLi}Id!QhGq4OW;p*kqjrOX9?V577~bH)cww7-fa z>uUj423L*(me{+!{*Pc5wW@2Vew1@r&_EPHzjzAz^=9Sa%PR!L8uCzYLyg(U#kWSS zbN&s#^UC8^cQG`r?}QSFnqwt)bVR2is0XHYyKmM^wxS1a6YM&Ue_pjG)?cF!oraNA zWIDNC99AN>g3BzEtZXtzMx=iwbRc11NC09=$5->95;xB&TG#mlUmEk_X2J-fX=ewB zp&0zpG_&k*LoC_0tJss}?ySjm-{=`1FONHL&E@@yEfLGWp$d{*@L?Gd-%LOFtSh5T zrKH||Gmv9cu#Q_W=a8|RiZz(jKQ0UR@Nu_?Oeu>9T7(`?tqAVz$U{BLo8RQp!U!pE z{BrzjQqr|ho~%AO1gicgg&z0K$Zo|07aHhNErfE$lk`8EnSOh*e0S5<^S8XQ0sHUa zwB1ipjKzX~w9zTr&&cwR6=UjfQ%8i|9}v1zIg+^3Mcjs_y58$_ynb`#*O7!z(;@f2 zCoW+6BCyAk6kjFDsvUHjkb07iN>p;B-#S6fI@%`IG@A%PE6t90YEd*gWB|=T|uZm85gn>x=0b{pq>6guK*~R zLGs#g5DHyEA4_I>e|KE3-9s`fQc+_gU+)*DI5AagxIfANV5 z_rq4MXh}GeZGSbSIA*_7r->-2@d+!SH(OH`TEwdMu$Rs4(mFa^^^uf&CVkHCLvP7a zZn>{)Uhtjlu^vcCWr>K#T$`YhL1OzkopU=}MbPiB!eH7<4d3%DcLXX61m|2M}TEefz&W-JIb!T3s&RT9sX?=H=B<*ulR({~xBl zGN`Jy4Oc|Eq#LB$4bm+g(%s$N9nu|2w}5n)bZ)vsy1TpkEPTIn=KNxG#&Pe(6Zg9E z3MKrzzHRusiTGQTZXp!D{=$je#_dGShR6KF``xM6t$Wp#t<-OzPk*;d!#XN+K#_!L z4V0S(I_}JAM@P6%>nKU}14v&OSQGGHuc&NV_9;^soD2X3dETPf)OZP955OqFUQ@wO zW4_^jB~)4n8Fsk-Fu9j3D&<3PKT389xwywN(GUI-ydRsqrX+FH)i+QkYdaHTZGEGu)bjPf6vyf-b4B?uMc+ zY}@==SoIs=jk-ABv=LMx`R#er{Y~^WJ#RCS0|f|uHtaa;xwMs34uDcObRwdagI#qE z70JBj@Q# zqero9!WVX8AHf7Lgm|4hWhQI>ua&&CKf`OY4Pre2UuU)3xdD^#$1_!`PmGT&WmIkO z%MKAN+i@foEfmvgH}{DSoKI~yO)8t|G_M%yH4YdD8f!}|y5&+|?-!J21I~k9bKf^2 zT4X*w_MpnOO(M1JOgmK8s#q1g!Zq6PR;jktaV57!!u?{o(%7}?Y0-NCpC0lSC%sW0 zTHKn-EplxZbl0RH9i2+3yiR8-a>FUshg;&WfL`^$s>bK}A=QCafJ2rcwn`X5NgW>A z1N_bQ(C#qnRZ4%It-SRHh=jS2AbQ$uSBtOtOQrI+I4Y{$=^(R=fY*Y$kBB#uD%0yr zA$#vcuBiRkva2)g`g<>cpT_HA0~I`q#Y6zqivM^#TtI!Q4RaQ$(lQbcRFp_=-r*WKGM3MZhjxQp>G|}gN|zy>YEaU(U;PzzMG(Qog8Gtg zxOh`WSy&N9u=!u%Dm75OSe1Y%oS=-o*==g`g{!sCBWEdN8)MBCF?A0$} z?G6ZZ7q-Ykj`Z#f5)zWP(q^d)50EVHHO8!|l>S|ZPfPqWpZeHqNYJad-_=(4*Q=l} zEjXI9=VLG@hTVY@TIV7B;{~!=D(wAXROuacvpQ32qSgNom5%PFKL@yuNz0W8vtU)q z0AMdlUlSssEc3aCD^qF9N+_I?ZTL*2ALB0;@Q%W8ZQVg?$za>m<0sMmR>|YBew|XF zTMe3g+D6edH8@3)P@O5d8xo*B7bO{KVqOI zC1*j@uK&j`0MAiGJt0IFesHd#=I8i`+_AC}zMM7n%t@h5YN;0^EHoy4BihC z=dl6CNG>=zO;xd(z10GK%b7B$U%sn8)=8C$6CV29yBVIJfoLr>s!Y50=(0SCaiBK5 zg5}ZIOwaf$qU8?SjDs;h{lt@OPYrgPmK)lWvEi*{*A?@G%ciU9R4PgYYzBk%9FZ?V z$K8Ra%Y)x5bbEKoumJA<6ASvX=E!%bSN-iGcKMmS5FBAxJhrrqz~AK<*r#MnRVCXA zcS~r?r%z^y;d%*zSKP`PztAKOImAAAeQHPF!KP8tgVZhwgFgq-eC0HO7=o9X^jvEKLaj0d`3P5Nc`a9zs`F^r*96Rhp@HyK zpFDu)(~>FpkFlE%SLJ{oa&}Q!fCUE(t#=sD+Ry0&k1ZQHqZ^i+qx>osY;`@@kji zyUSa?EJ$8jDr@$PTl|L0vRn7;IvQFof=aA&D_#advQg5csVM5G^7TLPfp|#$iOhND zt6F2Te4hN;o*&Thv1dC3&e-MacH;VawL72rw#&kT&y~-qKd*c{xzJ^jj*BNM3KSC- z|KHXPvYE2m!<*It6-yROTI<#C@8QikWwg$?Fs;i(%fxK-*0Z18Onbi%<3R%B*BX=g zcwC7iJ$QHc==mAE4hbOl(F5s&+c`j1BqP#|GI(tO``l~iINf!UY4~LarWaA1>NDxz zL0|nG+97-8qxH@rSNl_M-|cR6+3t0_e!{1Z{Qzw?@RLI9DG+Cqb2_zV+Ji^$xW0hs^-Q47LC@|6yO5_7;_hnPhyYlxU|Wgk40~Z ziu{VHd;oQpy}dS$<6kB#nz`ZXyl*rh&|&N`B1+6J)lq zMaYJqEyInR77fnNKEz|w?lVkwLlOy{5(OkSqqsx?<Ddp+JrvgXP(vG@by2+*p&0cU zpHuCsXC2UlceE-NTnrRsogKi`+_j_dt!0+vpA{g;_7%~@W8N46+KZ^;Ze+FXSL zEPu_OL2EXD0@8|~L$S+7u4i@o+I4+W=Jw1ILFps@^I6qfyIL{?oF94V;XUhD=(VeY z7j2s&b6OwWUUbKp>^HjE@Z|s@4p zR&F1b9obJA#knXy2I&g0zkp_>Q5mZ`QN><#;7L(?8sY71t*6h>@A@F%x_uSarG$uzF5mlu0Zhr=? zi)g-St6&944P+qzUDH4+`s*8=_%U1YBOcaU-+PRz_JI3trZljao5~~f1Ce{83At15 z_X}Dc7VzKvCv>!_wOM_eRf*(#&v zRh(dRf<#+BAXG+o0Xi+JTsv+L5I8lJ=bGa!H@ntbnQZ=5lr6jTyetn2$+DhWaU#jt zcGUe|;dC%ikUnU6>eCS?%P&-M%LYv{34MgPVm4nQ{j813;ZAnn;>^N4F*#toCdB{Ry4%V>UdhWpFRU+4df(z(FX?y zkUpYD>mDArCjwbDvg`CU)1p7`ok&wfsfM~gF8x7`PbSth>Q zb$s2Z>Lc2`nv#txQKAEUqwYph>Ap|#d%EpEk+FfZl?*V|<$mFUv+W8J6ZQm*WqDKF zOscxhP@hBMemsZRU-}Xoofi|I;}JP|#oVyKKIy!gIs&2OS<^Z(=)+^yC1atuJeePP zqhN@=TSgn67F%y)IW}$G93AZljvSAXkAz| zjV4;$ujspZ5bXAIE|>{xbK6=E0YXYcwom2;|1(Ap%PjYX)~!x$FW%{(Ud- zz8|ufk#@ed!(vVE!FzW3z83qGWPK^EaW4k}xh-NAoF8?F%V9*#>v?T+>tZ3(7BW80 z=y7{I_TX$eSLX-=7bbBm*s+%|6QN_?YBbKS++ya$NFV^+cvbyO_v8P$W(sPIykq9# zWFJ&PeqXlJ7=TH>*NhsC-`8&$-8P4%%`~K~oyGaF_r^Tt5+Zva5VTG)a0ZE#B!x2t zvUO&pfky%aJl7!N6_y6^iX9!zTnM@=B48VjInjoYGu6~-MAXe`s;cZWX3Cd;Dyp^H zAcaY28^=N=2#tB`+rEggMz-xqZS*cOSEtdN!5)XiR`aSPK3cpHH-g*(>^z8f5B&65 zUfjw*ety^7n{z{q5!QmW7X8}st~d?PUY@uDX5_?5<)=s)Bzb}IqlCjehM*Am%%kkBMY9(s<42=xz+{TaTB*J(%hvNIbHhKYL~Z+(=R;;nDJnQPD(nulBqfgW}YkE zGx+R0G(0-sx#_?$th_0-a#BPKtaL?#h0WfM?+oJ7n@6CV&G6uF(bo1hYg5#?U(>wO zUSWR7`)Q|&z!})%fIxTKk9C&0IDq9jI|IGdk+(I`hdTI8qpUGgv^+*<`|P3PeXR@x zTe@9Os}5+k`?N2@SuZ<|EH166{Y2Q8%5Fuk^Wu|lMCktbrW6hQUAL-$H4B+wmxv=| zme*)U^DVPIZvWejytTg#3JED+&`U0*-DqRMI7kB?SkscfBsBc=5BB$Dhek>d6+yE<3-w@!VcZkv3bJ@VV{ zNaf-h+<+R!rMm+DiDp;Z{Vrs8>)YouiX$aFvtrK22@PY#MY{jpcqS$$A^rX<@yBF0 zTT_pR?AsCaz$5Gwe?5m(>84bi$rCGRAQo6oR{#vzs=K|vbABV{o}S(x8k5<()6sm^ z>RgJ^+3mz^zF!0a1l1)9n5 z*l+`-dQmh3B=M#9{_Hy5=`dtd?$+;7e=o+mw~ox6vD=DP!EP7D16vtA*o= zlv2=>oQy0Wh1t~Q&Sh?S`%3c)I(0ok2AB@_^vr@oULtfhXQxiP58+QStOx%?ZYLX$ z&C^9Fr`mn_1>ZrVmftE>Jx2xy zYB58p7LLP!F}{0{L~%-)Om&7mocVNv<s>O%5zPhK&vZdA@7ZyB)592IM$@+lQ=Az& zYM!~rkuzzOYl;u*1sucPuUV~^(YCoQuXm3u4DPnv_UWzt@9bs zumr!~h|N<%)EmbuaDAU-gJi6c6Iag*#6|nbh+bX5+-ksm{iks;HPwi{iR@{Ku_Zj=Ue4 zGUvHc>9Ue#yvWXT4t!l52W;CFPdd5=zxTh0r7t4EoVs{#nn&gfzFJBL>4vCc}U;8R>y4VN4w9T1426`i=i zy}y~QhJ`6P;O_?lDlR6*^6e#>Xnt>JG$jC!@6_K-KRVq#sBcqWwbQ0l#j^Pr5!}Iu z_!A9XLPCP?;89y$UBbzU#iXV&jkm7M;xE-9VmNlT({#%1BDes5FWBOIDN$|WsquU? zBtpH$k;@8@>@Qf>{Bi}PIj8OOhbCRkz8y!-1Fdg|Y>fU*6Q@a!<560Im_4ZCV0wn@ z2M5vKC5Iv|j}fE1(dDH(Ov5evJn`A+~>W42*?p(wvCwi5QQ}s>^A{PHIFd1>#Di* z(IYzB@+By@TKL~m7pqD*rP|IlrQv%4w)RI876L<42QbuO%Ij%Ri)WX2I`(aX5Ch<>LEa?%k@ht(G#l2TMzIqy}+ zye_9F&%mXZ$!z=+5Z*gBW4(yW=0vShLLsGd(fNP10Hu%e%GyEG7CrlRe-_yWZBTOE zCWDJ7Qr#xtI&(+Tgv2*QHpgRCGSeb3rNNROqAh!%t(~2lCU2RQn#8}_x`Um718Q%x z+xjkf>vv~75?|Zwv8Rtu8$m~CZ*OQs`TDRj%Gv4DcDh5rl&-owA{lIk*y=!jnN4wV zrrt@h=xJIA2sG!`m+#AkWnn0lJ(;dm_!G0baV&E z(%kAv(gBQ`i3bO?I`F{ z0Jj_99YH4G=!nkX#^tpzQWO4=HP+W#md2POwbvBW9Y|gcgKSar3`dpzy=M|?om}`d zqWAu@MkqE?BS`8OE{ScuBJ_kNJQ_phA}R->kPWLg1r=2YVEwpBeXg4&GMOt*+U@}S zAXn(YWj;a0@g#0Jo~k}2EiF86N#8JxXyY6A>%3!^V61>}Cavo}>;~Gi-6bl@h_Vma zbZW9YC$4!~Q|7bLT`D zG9Ni@ak(ftRpag1H{WQjU}Y_^dZ>9i`&BcOw5*o0Z6r^m>|iQn~aXLZ1w zwaXU;W+uc8ZBN%PpLlDe{WfYa>9~m5CUKQ>Q^@uAtyGuul+SZA=FMWIMp@bCQw{g& zOz=38!Q^C>7<(BqCu5O7JmWl}*Xv^n25%d&W@1Ju9gL!yTIjqHatwpKnZWt45J&UC zHSg?ri^=Xu6XIYvsrB17SPB|b%due4gGou_+v#93 z4gH7y>l3p>A)VQ%QS_Rzby}Aw70+AQII61-mfRefj!#HqSuB3@J8%hDj4D`&cq!{# znOsgb(dgO{`S~+maG(?I2KyL%c=iZdL5MWDg;R6xHEczZ7vDsO>>SNq@APnYayJHTP zoDq%>aG1WUTP!Ye|BbeIyxzE?S*Fq(gFCi9C~3t;$24&q5>Tps358B2^NEX#>xs?0 z$Gr1z`Pb}P85|y`^R9xDKUq;TE9_^`nH2h_vjamz5qI7!_e;N9FF)+@aq0oT>Sv6R z@d3BnisY6PovL^~~W028w?Ccoe)J$`hR@B=4pAfs-XH zsYN^b1!g9|h2S$))eO!CaN9_IBR!6EidUEm;Rqd#&O2&D^9lho`+i9qA*Y~pq1JlW zz5VNk{BwrBUpYU!)}gkLCpm6>!)XjBx!y?&h<4Fk=4 zsG@yb)bsg+k|-r*RRt{#VS97b?S$ z<9X!)a=PUrjFoF_Ntu~zz`AV-V*w?4%y}jJU3SfCK=dfj`Ih2wxnZ4##J8~yllmHS zu*Zy78!72A^C!!Xi+_Uo%x77UN~R?h6yV>!DLPi}nP&`e-k@zfVb%{jn^fXbheQup zby=|+R;Nx#;ZfGWEgODUNM^tbnI)p!3?C+ITZL+NWp?NmCqQ;|G_yFQ=q5TvE~6 z!#uVfa%8T@mL?%BO~$}LNUBsh<=lD+N0x9AlDg-yBL-A1?(u#+IlXN%E3!}%L=%V?<$%w_ z6BU9LkCP-NiI+kO$KLRmFa`qn)ta2lg-;g8LAre#1#Olp0 zI#L(V<;6uZpLY{MIiI9e$dt|;tiPy@U8KNlX|7XHYsTm)Ah?A_7MvgD zZD1{7$vL4XS6u*=@YQpDnKP+9&l}<>9Pj0)k0UMgeOOPzjJGd(KtE@r#$nQS^mo5X zpn{l~kwfD_7s!sL-R4(aj)fZ6Lxv_R?pG&Sjx$dk76hby@fNck>xrpMY!FWtarG~f zKz+3X=eUP;w70v!fBdug5LWf>UyE|ew!Qa&DU&Hh$^a)@ zRbET0Ym$A{Xg!CbVfiGMHypq(`IdLHlx}~Vc}q_*rZK3M&4do<=EirO?UNutL>p$1 zk2)BPRA-;gXiD46;~pGnd0ho&99Ts6Uy;c>2F=}CL+>r8Ay^8LS2&;q%Q1;A_K9v2X<0VJBy z=iJa3_t2@KB=vWq0?T=!M?qb3vUrw*!&;^LSkuPl^H35fDd4XdQ`$(W<{iCBO$v@F z&rGCLjtv(^qpeTDkxvkgwwa^d=h@GU zhYrn!)O3cfoiY~1C8S>hjlQbvqH_V4#l{9&Xhclkx>JtG_ucyUPC_V+M`lMU=K>Q# zv)4ABK=nMJcMsu5e9p6WhaccXtOIj2s3xeLA%P4YsdT zFromu0vmQ$v6AQUcc5Vs@T~Z3Z*2hySt0j8OObx{qWIH_aIz~u{p))#o!1x-jWvCX z&BmIcXgghV^n#DZ78=3mYi-RZpRR)*&#ZKujcyxj7CC=D-)!(5NU4-2D&lf&$-7{B z^(#AaVS#`pfN2af-(nAG*c(MfLxaoko;UiB^?dhl^n$=89U2jBZ3dW|G-+5#&yP4jTG=%DXxp}`);%H5*KR2Vf3%>_N&^iXWt%ec<@!77Y{W;FTylq-LO@WMz0RM8GkLOMwh} z1e!u~rVg8yZ6;+n>ul4Di(8>Igyurw>zK>X7tOj){IwmHKl&psRU&%s+x)lNXsq%) z#qSQRX=PfzMv@Sq0)pO3FXmq$yetscP4z^&YCpY4c`Sn#l zx#RDv4}+|<^f9Zl$bv;yS{gp8`nWY_kWrZ~rRzuF)XDxv?)`%2i5~KU%fL%`$@lW| za#5QKtt1ClyW{Y<2Hj)qL3!y5)0qa(=Z6c4$ozS(1Ur>Or~&f?g^Y`q!oGF8jm!J5 z=y{(FqVviZ1_W{rK+?m7k~`D!2qlx-3AhZ6y}&7^qHM_1fz z(+Mx4pQvC!AYunGoAW>|1xXLJ&pg*>0BqE!Uk?Fp~>ve2$QdW^B20U;?S8SIF|E>)7`JFXkBbNF{d`9E`F% zAx3;$nekC#SJ_OgcjLxAw0f>mz4XqB*qP5YBQ&`u%n71B(~<#`nS>p-J}P6CbeogbD_=BZ5V1 zM`TKu!?H10GZ$1qC#~Q|L3^`n=BMj+&ohYPrr6f~nmZtOfGnRlxP>4gS<9I5YREUD z8jw{=$W!g%6JzGwx|L(G7dnU$R5dRV9G4XWwYgtMsS%{%+K?as5+GQq}1R^D@ zc4vH#NPE+dI@i~P!}9y|eRRY&9rdA@)Aks}90)IrmIrDOb6g*YI81ucOL)gi(wQPR zSZO>mmd3MhsE#cAbqSJVvfW287?LHyUNT2kH?Tq+RIxVJ&9 zsHiwKqQif%mS224YHn*o)o^VrSAHXM9l3`y3{alu zOyv>|GFc_z-~{KpZYlq9-t1ow%$Z(y?Z36M#$Gd${KFy|u1X6U?N>IR3>PqLxN0M( zLtJV+RyhBTy+>RQ@$f+2we|hZev*NA)7>~IhvTu!3ySA-=#H5Dxlwr)mzuajSHF7R z`M%br8=>7cxJxcvqXJ>PQqx*^k?;;NL!$y8Jf2pIgjv5h!hPB{^bOS7oyf4*xSziW z^qHXmsYSVeX1-}>bV^W3Z-RW?qaDY1V*r*m0U+0HycRUtp7nUaCR&0yO5&Ub2Cd{>lLi4R zsd+hM@%SKP@w8jNGH@YDAzUK{0SPIsN3fOwKBj#0qKQe#jA{rdlX6?{!FFE{!sa|I zMQA+lZN_CXAlIo7kd6uw3ZZ38lP0H7FDmw52gj@@_%dv!6H9OBVe9HGH5fLTe4G_+ zqsAGi&2nBoBxPl-n^nRzFYyMbe;|KA%*dDi1TWaLM{Ti(bG~<$NQBAcwyNVnetV_S%X|3IjLzTL1HwfXe~yc+ai%2ePzCnB zqPE=AM>&b=Zidpdf z4T<_&Q}XM3Pef9!nd=CvDzj66Y$yh(iHzrJ=a^1A$VY~n3*>`4_-p16E*wWj6GI0q z7FF^5CD}CVlI-AMX*kW5{j|lAjxsK&n3kQ}H8JPPESWnI^e)UT!yH>+E~|q zlvDBUaZ1*{3y#WZf!d>!627|EL7E?Ql980uM;625Ap9qx&Ii|oCW`h=PqFV;H!Ho< zi!$%{bT4L=#>vSka4yI(_#kpIF#K$dmizh)P2BQ|n3XV_t(o3kU}Sgn`-cFER^IQ) z@1LCD9;>u&$9a=8-4dy6eG`Oq@Fj$zcl!`_m`s1)?@4`63&Remsc8f%z}(OCl7Im4 zyr!mby3g(Do%8e33lgIdxiMdq3Ox==B?nT0G{W-E6<+=PoT0*cYRwXw zG5iK;W`D?z5Jq}|a3sEma1=7PA_8CE4@DQcLBs+LdO(Jz`&(?l#UQ5om{Ii~V z92rbrPv@>$Ugu7L6}4gO=QqJ6p{Tnl+s%3h4+19Wa$n-OvCVdBrxPy>;C(9aL{`V! z2HNKCyT1fB5ZejRJ{*#`WjihXMDaa@^{c_t7S_)0vx6n@`rI}dS51Yg$Xw}9A^t0) zowLrwY?=UvH0}(kt$OE0d*(Vcn+i=Cr`ut6!-=hsF2eC1)aS)hl{4v=J5Z4!3ec?u&Y zj?LQ&LAm1ClB#Kfd^Al}ONZCMi{ykeByc`5B4T;56B+W4GT? zJl>oj*!Q7atga4k&Z0aewyg4WJL;-jFXHs2r>7IGiq16Mjyur^Q@$-#ZIIj|9(5` z%_I8pXsdZByy5m0Hsj*LA+i=i%~T5fI2z)u(BQU&GyIHzbr&+~y4(rs^E2I~<-GHj zY2rv&Xp+XjPGa=16070=AJ!NuD}IEzAz~UD?f~>I2|GAgv=0M7b528HZ1to9V3b$0 z)~lNrmysdi1?<#{hiz!uaJc({Zm4tO*!AjvyqviUHjc&xa-zTu&zg*oQd{U#wf<_@6O^m{~{6t0D7uH>*{VY~R;N=XL%}Ooi zicncy^2nr!krY^y!dO-KeB0w;yPFdIdsWr|wIo^emv6YM=UapQAd8Fl9jf{52-$gE zdmR_fX%q$~=DOR9JCVm;Hr7zF>8dag52|FehaRVl!dgCZMEe2@wyWBj-*wv)SFWUib)wpysnWX%%W;qd)B^XZTEGQmTn$`n9VkW=Kvre;pibs2QMw1Spm@;f{H z)Ya%`^tQ(yIDNe_gza8dwVFFlQxw~WSCxcld>>#UdIkZ~ZC<@HZX*ZM$Ozd=6Jek8 zJm(OgmyTRKb1PBLLZa{JqF9g#i*8#AQSi-)zT}t#G4bM(h*eK1iCuTZ=|gT9rpWhX zx85yLH5Gh2{a$CVWB|+C6M=mzz8cWxi6Z@T1$Z*BsKNP&Z_DLez5VUUZ2f~l>>IVD zp7s;8SgAsWHb4}c+_qT{UZ!Ja&)9d>KLrOR;kQ5RLE;1p$jdWTwB2Bh@gxyDgaRi% zB`a%GZSB?PXGtIOac9Ds`K88$cmUPBVvdRM2uFy(VF6lRYM^R*!h6sPf67-#Q3vIl zBPo;B_2w%=%nMp>S;?QeXER1ZgCZdytLNIBzag9p-YX?Va5*_3uL3G|zATiqkzfUu zi8il=BPQl_F--8HcF1%p&H-+e4Vz(ahw@<;mT?k1 zj()F>qRhof#XnqLP#CSoox-@_37}P-!kQK|nEb~Nak-IYUOOX^c|U8Q^N|uRQ|TXO zbROHIZijXmT~Habrj7i9YN264!u#V;+2ELPnu38*DC>XwyUHwBrmOx`p2H*<$btf> zpxF;`#(1=Q4A~Ha721Ef<$t{=qQzslMtDW=)g(L17lgXhq2AWD*&EpjT~7bvLgJBg zxq{8NeCjUG-#*P!Igb&7qFQM@1W{b9<<`oD6%nlP5>A$vr@Z8KN&0g7I=Z=;{e$gC2OC-$AX>Lx=`-)loP!w58&ojNBKYuZ}Qi$eDmZHEI>1mHK03%i)j3D z9E&l&E3)e=q;aKwYl`EjJ)=du!TuyyPHt2_iI61Jg@*-Br&mVQSOoMKq8v_fk%s=B zXgvWOy*4H}*ObQXJ!1-ecdv%Hq{7jh{daJP<+%rv6lKtqdFLVP`t^ZZtLR$x=H)(6 zo2XFUQtgib;(>~jK-g)iOlEMvJRk=qGkWbNmQwHD3UA`L0+~oa9rj6>#iSyw;hNaq zY_d`RlpENG`%?wC9{4nQbAPPyF}E=zvlxFAt!71o*M9?}m%nziTK$dYfH)gWrmMji`8Qe)tfCN>8%*Sglznc#S+E4Egn(4Oery$3y67&qU_@eY zEQ08l+a-6Ymb1Co8OKI#rTB!ZW<4s_X=PMg!7raI0h0lS#>I~D`q<^rqA6Df`Gd&__eOj@a(82tJ>R>>2fC{WXc@8?Vw*5?q*** zftYbucN+1gf(_IW=wg4}K07zfl|uWvfCotM#b%v-yaw{jD4Cm1z`K_Qb!s?F1|I-` zWphfFPk*x)iM+e-8o&AJDz$`B-I_c)^TXH*Vj3Spd@wsyZ?{J7>_}ExTN2xeQjQc^ zYCI)W!)ZCUxVH~PCX%J#Ny({e{&30Er zj{D^Zkidsot7}Nnb?TSx|43LWTl{o_9R<9qmNzk=t2fb#kB7|wZUy?+D?^)9c9dq` zOnjS$jN#!AGZm|1+8Hn<%6*5dFo6h;_K>~Jwsb$PKq~E2Mm2aGIOz7 z6f~D$Dl*?krO=CSdH!%-q(Wd0wBBH&#-)T=t|nH7y|ExZy9hyF0B@ofUq6x22XFwT z%=rByzm-xvUCx&%mE+P{y-3(pEb+5qD5oHkOkgNpF4r(TDE&C03~)N`+uKQ;?t|w~ zN2W<@=kbmU=7zW&jHm@u3rG5$`?Y9<*7|2_!uc|Uok#Oj_LC9;?)!PScSkj{RT|a~ zBnRYCh7MqXr`gkR`M0-O9g&SB+aO*WZg%$i*~Ol12gf7(KA3OF#cQ9YUrSQ;3p;8? z3nvVLLZ?IaT`5UP_!+-kJx8~}bq_YL(Xu}oK=H))`^(+SZ;JpjuQHURxM$B}N#YS2 zQ2X&>ojR|uyqJ`!>U5`ZfFKzN)i)R3Zslk=!p#}p=9v6>hGi{^i-)NRZx@}6@yELG z7kePTQ8(xd4$q4ktLv(v=@3?wEI(_NTUY{n5)mb{kgdjxV$m8G(&6kaRPe;OPSIJo z;Bsi|UQ0o6zWH#5Cz)xwJ@l-$FRx;LdZ%%cwupoTW*R$j$M(g>C9bfT*pEakSq%*y zfb`Vz`i{cq=4U*lWB712)v%k%4Rkp>%|$X=y2!LQ=mFg2c|Tc9U><1qT~gFSds~>$CwXC7>GS5hhFEo^%IEXN^^_HXUF4Y417b)j~^q>3CWb*YzOlNXd#giojwAXD*WRV=Q& zThB4*X|Ez!5I<@#EL^-t+L>dyX}yJumS5c+FRmA$k(!~FipEI?_ZogXO{EIfFVy3M zH-69iNiNIP4?im#Xhc^VHUZMxDwF}wj*4Ebkhnr{~I_14qWwn21fA_{ysBSzx zA>D9Hf_+}CEi=RkP-=08X+@;0=$p!6comg${mg&C5=qkJ7#co9`eZM@7j+es1LV+b zVK&E}JLh^ebthG}Us9ULJ+3~4wBNrw9gUI5bmFO7oY8Y@aVtiNWn>}bNt1tM^Vk1% z$aTU6+WtoO$%Zk-(WZ~dOo_@G6BeTL6c^R5T8O462>b7kWWCr-8%B@X0HAbdYw2=y zH){5EWN#u@V*yA79sG)*ge^ehJD_YF&N3EpqF<-ccv#NOd0abncMtLzELES4i;eBK ztZ40T^XBFNAeF}TU{0Q_S9W?ViBVknfHol!atv!sp|>Hn^=Eh22nQXqTpGvM$O$9F z1!|X+j+h^*I~884#Q0@?5^)26fKfM3s}>Kr!MhJR)9XUQrsZ5z6p?u4!2`9cGf$Rr zYiX)*@rpBeC}_3p7gogl2g$w;*aoD%xg1O;GR3pM%GHnSS`a*W_7U&jfigvG;QJdmqE#;2@ZbJ4hJ6GT#Tq zeq~Z28)mavhxwa8@N0QKPf>;w2>0d7EiL_?lT*fXwdTcUz0{H0{>nQg%Pk!k!H8C# zyfn;^b(O+327Fb)SAsngt7m$v_`?}kId?{yS<>ZqIb^(lrj*u9deh55R+iQ$Gvv$ayBS~+Ym|E z#o9O_$okhaKbYwKv}2b1*Nc(7HAP!@gZ#SAUJ;a_nclCUiRSicyRM9us;k{8Md6+fW~}1zKeE)W*~yAjdt?`@Ki_)Z z_YEo(Ywcie}P8m%1~0KqPIQkx!IM!20ECzP%_!jiE?z!i+`-Y8Uj3R?$+esX{620MMFro3&l;(+ynf=_ zHjpGYlT3=Yo>5A^JgO>06Gm5Q}gY2kK3X18ut2y;;s|-iDI{Gk4mfJ@#e7k7POjKhJQSq2P-f-ZY{6MeR(2h60PZMpSiOw z)2Wri5m+bv2^MuKMov^VO%3f)99qvA@p{P0LZ*L`xr9SNSb z)?cEi|Kg~Jk)XL^JkS-=$nS!KP(c{hc!RIww!vF0n=$Y@GVm3X-tau9uW>fL=dwOS zT-N&;Z%iZviFg#q?_DU_DLtne?ND_Nt(MKAa95)AP=`yL<3RSRk4cs=HIyDIm8I$I z1o2bK({<~!{VGpYE69MfT-i7an+oGUuZ^~z?#CCDKb6Bit5o!RBGFiT{`DQH#$?3c z{HG~32t-9j1_phvHPA(3D5MbiwJY*$EwdHz^7ZKfrRQyv3ub+?Ufd1V--M|fGBPfx(CR0m0j_D8Ps z?eO)$bWP6em(xu#`P*r#C;P|GT9M=noGF@EAOK;JVwUA28k$KMUaic0?hQjc4cnlw z-fDq0Z-phjkIY)*_6iNRUHQbxjcTh-14)T&@Txk5^RiZ=hW-;moNLqI&+~ynHFPqg zr<0xXj~^C>r~Jgo(k+FzE#nv(-hw;d<>a$mYgQn^z6f$h{C3}mOmt*`@}PIq)5_G~ zwIXNV-8k=|``yTPRLh9-RT3#$IoQBTD&e|X?@Kg+944S+;y>~az&OI9qK+M2N5diK zB3uRsfQ|I&3eErpD^`T?vB~{)WMl*>Q4&d2xgyhQ^`zyu{N!kFz@vBs3W~SGM&8bv z3!hFKlwJS;SY0{4eN{g zztQO`^Z8H za4aq%-uf|c&J920mXOkZZjRD=gDR_cUGekATJfwzIIE)jc+_*llwgfY$7R=P@tUtS z^)yR`ytfEiM**F|=0;dl1j^{cYn44o#7z6wl}dK;DwFfEF7AenT^KVcOVlvE@nMU?IiX#wf( z?(S}+C8fK&rRz&~cXxMp&%tM&cYg1zHUHsq!M*pyKKtzbiCDbUemNTcwUlwp4%z~) zLpaf2Bf=a3x~nE`)*F@zks@#jv%6?c<|Fk8pK!sDjR}YOSeEDZ zGCj;CSSrBlkQVoL_iCQTfhKQ~T*5-M(>$);mUhV0Eg6gO5j&!1d}D3&u#$arjPh+5 z_vxW|se{pfN;N61>-mI^N34j5abG)g@e)^&@J3#J@txeBq7&WwmccLZPSNWP5KE%= z4$~gXop+)-axDuZBTDxFDeE~rV66ZD;2*pNvt0E0e)l&|_QDlLbz=B@1_$DE&kwyF z&U*dvXGvUtW-?R#10xz4)MmeKbE25rp$2p6%iffMZi&>_ALy85uv${ievS=e`XCd| ze7HHQ=&IQ|Q?BhgeZ`xEa^2Th8zh7=$e>+v>I`9cEBZFWVdMls#7g}f72qK1A!f5x zQCeKjiJN$MqpNInJ8-?ZZ`AbXZ}ol$ud%l@Mo+Qqs(C&*v{{fb38hyc>sEo{{a2lp zp5Eo!<*~_l>e0*jvCJ>R>S=zhmG1$1=9z^sg+~KVVBv_Qf)LE5Ns{4%JiMx#iu`s9 zo)xQ&I<`rVkvF{)WzCz9)8$=)9u(^F?_9N(R;@~?#A_UY z@uOvSo;B!~HFl874~FHLIyPA!*qYSFV@ssZ;oeB5a@6j5hha~UqZ8juyxGuEVr>D` zb0gFSLrgb*?fnkURL@$jNw=W_&D>M^FGI24EHyafuHzDaCjnV7A~sr$qFj3koLy zBiKrveuV1FqelF1Q89d{jlFxl#{!MA)gWQbWq%SC+abP0#~`TuS9p?xF2I3rBdsbv zyy)m`anC&`bM`YSNy3S=`3lmMTK8v^<%R#lQRq}3!0&f-dOH=$cva&-!_1aYP*N9; zuI>c5A`1Bh1vRUueV-DO7BW@cQK{ zc=b3l+nveaCzp^XC#9#xO>=o*Qzs)MbKMb$DDIC-n>cr{8Rg#4i>~g%4Gh?DLx~)C zI@;T*4M(qPZu1L3n>vE5L$aRdQ@cqRDZ%sEyF_T$C)Arg2tDw%b9=ymCh23a(9oH1Tl?ev!b4GP%GZZxpV0ZwvN+{ zu4HZeLHnOhR)6Bv$*bLU>yf{t;b?WCfM`z-zF=opP5<~JWcm$G}5$A?=fO4mKw zL5@9gw~GSx-ih>t^{p*Blj*-=MQhVduZ2Z@Fn})%_x6}Ve(!JAzX(&!BS~sZ;9hPX zDfGktd~>EwR3iaKaPxNS zSBDGpj$28I4y(T(_p|B{Ii9qEOS`;^#rqibYwbqG>S)KN#$c+(`|xjp=)gK`18bHK z^wSg|4wis@JX0nFPxO5L!D&?$9fmuEMMMy+`V1SItcks$2ju~vdXT2P$~X5LA9VZ@ z`dX7j%BnROm9JZZ%AbLVOGV1Urlz9Px0Cb;Idt*w2?S_1V=XnsVKfaXCC+WCe&6AB zXXNJk0|~|Vcz8%^DPRk61f~8-`^370#ptQ~rW z|F1OX#=So|5t$V26+ZkvZ3eol^vZaboblMPjZo!YknNd<5HfabqCnt_=7cLu#Pf|F zA)?27_ce;kL`OcFZ80k-<~H>o@2$j3)=j8YBIuTIlwxb`x3huG$~p zn-IuIX}4!_T*z~Iu#=v)~^EW5y zBV`7GQCpAa`h)dWh}H8h*7b3X&Yt7r<7(2fnMIeiwY5pTo@TClx;MVh8SaB{v%(!s z)@7vF!atBJ@hkJ2jZ=70V%uL-*sAWoM-C z*VOZ%nD!9~J`G0SW%4(&wU^dbJkK+2z{UOc)4~6t4tR|1d}W}3iXfqF7>$%_@AOq$ zZjqH2Fcf@G{8MzOdDxakz-cHCK8eI9zfOVi!UtU)sH9Mv&Q?UHeK`og_;tmO8-l~= ziSPC1glp0Kx-aGFt3XKv=p6mE*%|#aYve1onry2D_=C`C>S2&OtaoBH7`pEo64n26 zxkF#OkVKqOGyO5&i^3Zhz8`K?FBQF}t8A^i4}dQX`0vO_9wR6I64=1^ ztjJVOtKl~@`ee*^`{g6>8FBV}+NJ5JFEd8&lw>KU)a+Q`aNR{hRFKxD%W(_p`Jc&l zpU=q(4>4DT>@1kIBQbbiG;dgeTFzdLkr-l(Dw+Vl=oNqOzpmKPf4X8F%RJLG@LoVO zLCy7G>HC?ATlWnyIjPw$TICadnX$08;DZ2w z-Ilk8IX4ZIE{(o_{|x$LRB}+pI$<&`y5?Zl;u7i==rjW25ArQh&+8?B2Yl$ff+AD` z*oNfNwdHfX{_Wwlbe4v+FMfw-JueT5Vlln_vyGH$8mLq#(c_@rK(`0-_pm5`-5=+^ zs&pCJLc$RY`3-EAB_t*B!R*n8^q3+I4F8@viNX+h4vjHik5v5xeK<6RqI6cl()1i& zQ>d%c(zjN3X!zC6U*sE%QS^1sM2V5%I$1L{vK5`*#}s4`ZkB%md%$5mOn}v7YrX+7 zqClw;=lT42!p5#O_9vTvXSjo3IrJY$rB)3`K-*5c38VD^gS_<#ANrrfiWp#2v|Pq|g{{tFL;`XxWXuP=}&fp=F& z`ahJMq-oOWzPyoe+eSuSgrBAQzSJntS~$Z3kfE3H`Cm*c&5e^b544#IBh7)(fah>Y z5BXe6_9#tK-BXnh=@6R_n2)pO3as^Q}HPEZ8Yk(wR;O`+RDcxdP@)Uov zvld5l=@&GFiC4SIC(jo!=%5ayXa1VZ(DL^WzV!~YkxRx>2K!t0!<>e-d;p43|6IOc>_@r;Q5Rq@tE>* zPoNvLGo;1YQ4uzt>K~_!v+j>b6e@SmmhT4ch9yLjUA_>%I)K_^*j+0qY2<+;H*YjW zUg1<9G?OorzC#*9o4nK<*xd+@$adJTd z^p#HOi<9o9#qikJJ=+64tVTX_$9f#=tFhm#M>w8r#N6CO$-gEIxt`6V9If+D`+#!2 z7kC4N$!+&YD3!irj5U*_&=|#rc$}+53-5@WX0OzLlV|PZyO@wIBO$;d+lWL zU5Hm!*13o(2lw`;c`P~WGY5pjIs#%1pljZMQ@nNNNHSgNRk}t*t+RdX|CE?SLbGqd z{E5Sgo~_C>ts*mayy9F9=&`t{0liT{5s~(-Gf5RC5q>3pg$<$Y(xzZx&kwuZKP6BY4)znqYj1$w&|iuF2({lM+2Vrso1*BZDdVaL)V z)DQR)^ez&t{Q5oed;Z?b(ZykncCuehRPLNh@A2pzrRp_`5xYS3ZFpWOPa4k@J?3w$ zG@SjK8sxBHp;dJWJCCoM>;qWrVfy0v5%iGYeTFbQ?XqJ5d=;KCv2a`{QF;35};^#1~A znpTA_-XO#uKBEqhp{aM&-qa#VB{-VYUoFkg&uiq3OXNG@9Grj-4iBC*r4q%O)A2I5 zT%k%2kUaXv!cu#2@dY`wi&xN)CiU;!L9B?9g;{ajXV%%@b8Gx#^Z@$>yzBGx^K%Q! zkf(Z8s8lk$a?Ymk9^?9!QaajXvobqyZ=8ej!d{JB|K7Y107l%Px_on6Kov);`;I8l znvh7<>iv-W?R!~7w#+OpB~sfHntSI3CXuF{Eo|!(#~@7h_<;n*@VGd9^+B^?Ns;1m zjUo)e=a0dBull#RjWO+642B!o>1VychNRQ+=Ypw1eBjnATOe=!x3F-+F3cA+dH))4 z($I)~_^?_!XR1JfMvg|OSN^Gd#(PHR&3&2)@C{U!4;O=k9Hd8%Jc<=ND6`PwrNCMTc6|kWK)^hbme!uqqt08{LF{7P!7bP5ZOSf z_#D@#JJDVbMyjk@z`QOhmVdg9B#|~y^sHDp0uC+o6aeEGD!7y|o*Sgc`*!e_W76$T zwA|HvwyXUF#Z%j@Nb!?wq}EzzFh$i&&DXuykqqU1>$RbB*SRX7pt}^K$?I38$S=!V zs(*EeHfJ_3FxmX8#k5)uvGVWVzY`B7<`K#9ljk)IhrA@m)RW?t44aNyj`nWrAp@qIlT$QnN-ITC)2h>5*TQHTXT8%*~mIPU`+l za3H!p)q{Fu|C7)QsQI2rQ+58E5eFf=WUyV@*X)J~i;z=1_ zFQXMi?e1cRM}&%CbZI^y_Y7vs8mK8=QRPEq*&9uTyF@V|R8FInoq%6#w~tZ#8HoMW z)|ygU|LlG~$v#&ElxyJlg__nky1F%fMF}OI-@7@w;|oe~M33NjqM&NJLq!iwq{X2(9>adbp)TLdyUpMf`;YB`at%~6 z5go2^isg4^^8l}b2vp@vA6P5&8&rZocOE(o72BDyf=mslG|{-;+R(`SNX@-pb>-O7 zF^gjj4|^Nocrf&&-O&=kOjfy)Bc#eS9QDEaEss3mpGkh{pBdq0R(O(7<*>|>8ZvSc zhBPZ23PM>7KFrDhmKen2)McGxg?7 zBVsUhLw++REgM#A7VSX%(Ba3WbxZvfUF&yG$xj)cM+>LDRDkZ0vt;r7J)xQWOEDDp z6>5E0>rFKITBnVoCBw1BGg>(I7GpI}L2T=(n#1>?I<# zi8+-TtniS?gd>PAoff>U($XJl?_wq$X8&0erO_-F$&VduDw&y?k-6O%=F6{AD+)hm zjBF=w%F33`9Vqla{@}+>VZ}{AG||#Ll!v%%3cr|6WHeK?z)IP05$kk3S~EAh{myu? z4a$(q9g3nBY{Vh6VVZPv-@QpOe&{pJKBrwxoTO8bk>9T$JWMu~f{~~)3o;`=+ z&RwNb&aWaAxlvntDyF=~dGO_+lL6XV1levcbh0hdCp_2ADd9$~Mfc*2v&Gk$?DU~H z#dSp`_S>z}ed&#yPISGEr&nh=h5Z59h6;U!*EcZ8GKMw&&RCr04{-GhzAXl5 z@ty6VQi9Z9CA6qkdhLlub~-5UWA6yg1o}7b7q{?Yw$g+?udYkZ#m+B#v1M6$BlGxY zzz*g;C^=TdORYLs{k5G5-7huOK2$})ur)(y>=AghgJc1V`uci z^+T3BjPv92d0Rc5fhB(|&!7ADBxB~^Vfw1@ZX8qOnl5Y+)C^eTg$u4iiy2bbR${jm z$n`j}5z$}1r>KD>OeA_T4*t#@lPuaJ7m{<0`|s>8)9T+oN(889RT! zTlVg{xhY!6*74fqcO$B4#iC1}cLuU@fzjXC*^}%S9dp!hXz{^Ea9Io@T1AMLSgINs zk&?+3{^4gGG1*hBqD>i$$+BGVSPp<_HvEw_=jaL7b*HHv;xbe0w8g$pP`e8dS1fkk(~9xp8pm+ylcdRU^--)m%=(pX=u8i~atHd0&8JIaRt!?5iYEsoqn zS#SM)w>I(i`H?&8wnNp`zWfeGfqV)7Ng?NHDQc>C3Pn`#f))j9+Z?^z;-O zs=lS+@$@RFK#y{tvK0~%hRn&E3S7~0ayAKV2~TX5yb$wi=)Dxc<&~Wf+A58L46q%YGnFxr5?6sQ-hUE>&jFkkW7kRYf^P!z}sw0Amje z8|G2J%+ZUtKCX?CNHBZmxFn(X)t`b~y<{%cxdP>IP6FWAPx^{naf8B>a_-Q>(QBq0N z|JM3IV+{?9kI%a1BBqxKRZzt3LDg(qf&Z~s{rY`M@C7012SjCXJWe+$|r`(JeQ;-&UAU75CbGfEYLi1;+`l>p0bCWMnA!7k;TU zO^J?{Gfg>e*n_-R>1lrd96=6nA`FH=n&Vn&Y`76G#W1)#(SHk76#k)KacpU$a8|A> z=+lW!Z`ubcpW!UXJ$ixtkMxjXz3zM3!I1D!7Pp2y~CpW z6I5}X5$!<;tjf>hRc{r3EZ6cpTsq`5yjr!WX!A6H^C+Nz>nX~$$NvQf_ENfCv{E*z zxVU&s=X)N16gz1^Y$-P4JeVbtSz28b{9B$vxiN7o*&P>1 z5+Aj{{g6o3!RiAxL*4Pv{FmU*-Ug-p>~B(ui;nhsGeo0|s4+rVRr8q_k7U;_OTzgj zue2EsGwo|tAm8;7sT$5dC@Ri?LIbMv)nSx9(AH8B^E|y2y4-wn$6Kta<%(l2!UwsX{?GzGsKmc z(vLpq`NV<=7z_DkZ(q3qbQ4CIixfanQZM57^)=%tM!N1B$~vUw*UHLzgNZ$biscSR z54By75MF$0YJUiXw}{q3lTn#DHi1`1r((R3`S3`-?TSI_+vl{Y34I9K3u9^!}R z7#l_)Kzne=Mnmk^EZZ!LA~0GY@F9Xwd}B)8eIv~ew_Sr8yZTKcGs#6Jv*a_*5U6;Q zdZORlZIlRr@yUg0N>oK&s_!iuAD59URKO1&6LP(sL5KG9rUfEfaz!eFo4+0m;`x3zJwSU_Z4=f6xI;MvM0a*{N^^(Z+?OUHXEHY-wSAHXk;9eDUDp_yNxey87Srayn9$=zqpY;l5xGUg<4^}^s9V`B}Zw&`r0x7p}QTy6=z1+-Br|Bg&csiUJoVp0X2Eak_RAQL{n-ROM%8%{N%SJ z_b^YcuAb0dWwKC~H#kQY;Mt?|<;$0V?dcjc8uDxgqwRhI0ca}HGOFF{OKIG}T+@`Y zGFkuxBe6T0X#@3tfjMsti^KCb6vH@&?xiR=*~b@Kp>%sEwLzG!?|XW1ei|)R^?0EZ zky#_;m4Hp|ovW&`$PtkFNsJU!{3OvsS9W{yfcWi3BpO~c|9gsFe`~)#QBeb3Dff6s zu77^@ZLEgmNr%>^v}gcrBwx_fGsyPgP^FZApu4nPU?2ZMOR0pdeUaKF>Fb4^amgpT z=x`>wZ^vf6<>t}x2P!0K!V_(A$(D_uJ;vFbGt~BJfBixpS94XC4)i<-iHUzM7V+oP z63S1};Du`?ffxMY3jI+h-GI`jGQHeIN0elwE;1@wh`=AML>URg>Cj>ma%$9YY5bPX zswPqDXYDtmpO-*NHO7VE?5EV-*BAYSuM^`^QJviaxJu4}K74iGzJ5 zlzP{WTRrs~yqjLM3`NhISU#%hGws%4CvF^Z@E337Rw66wGFPj_?bvC#1+h*7s_shF z+|c4n4S^p&pn)pe{r#AgZ}O+tN;I$i0&5IsKZp>5{_Gu+PKF_+3?J{*N z28bJdqE2;hxGWeNe&3Nhu3Z&I8Y;DsqRp|&vXwX>AK0khy}T!JuGQ*^6V${UM&W#4 zYu?$ATK|I00MY&dE}$h2rVhaiKR#{iTyHt`B~ab|>5GInnEDF}&>^}m5PpY*n9-C6 zj90nx^S^0nX?fv&G)I{7;d!)q>$v0C@>RUXMn@siS(7#hW@ZmBL5v;f`wjn{ezMN? z=?{%PrMzhJ{MRn|pmzZ+IOY1ec&=p#g?3|wAo)qLt z=byH9m*I{z%AVrg zps9WNI&Spy^E*LZ<5?mUsYnZfO~R}Q+OEH9&o;wY4L`6u*_aR0oX5XHe3#4rdLrEk z3f&^oGpWlhlJp7tLSjKm3Q)^H>ht~>stDFc>#(Va;>qIaa-Gb4g;~fQyHYS%P%L!TUe=>Q@=8|jVfIfT#Fd(a$ zm0+qCpFVpDq7aQ1$cy7G92zdt8-i~} zPRDm8Q*CRamd6Cc(Jt6C^$QM1LNuSqK=RJ6vkte=jV!Q}_C0x2BkWIcaeYt~86^e}S4wK&b9Qu@h`=W9 zUkw_5{OF?IocSANXFQLP9hI9>1m+NJHos%pEX|W&2g?M>)KUn6TnGggc zr~|HHG}RqILuxF#AmXTmrpI_DBN@HOJxSg0a)nFvJj~2&?~ODrVb?TQ0wP_3ak80J z2Ypv>t)qI>)4+}$fz=H8uy32Fn8|Vp^BjS+OIeFlu2(uTq2a%bSfWwwyG0ALskWcu z#hfkP9igD0@CpgZ6N*8?*Aa1cZTfF@l*UOMBult_#4?1mb#x$IU1xZ2=&7#=IXOeA z&>lLoGVb1g7>2`6;Ih=Ho*S`k5Cle4Xq|0Rh0@P+0YA24Vh9b!rf7B-%3VrA>E{zT zj`Z}#h^{}l(LD}EJrGhGuKs9kYuj@npU6$c9M9-Pw806nt5@&hCTUDytSrL;;9d78 zwPA+ExO#6fb|N~xR`==BDUqwOLyZZ%IE~$Rbx^Xhmdu%D_S%u5Jq)Ngk)6-63bRE< z;yr}8gyb9e?g|@_g}FSnAwe!@_jDkPF(2gd6V4`w8M4USlJ*lv9&83#jV&5pp&xeh zu0-!G|8dVd2;XM{GtZ)e@-n$Xa( zmaNY*SS%Ts#Q$z_ZgARZ&{WUk&2O73bSjC>XcOV{-y{aa>2$ubxdWHm8GV!anzum4 zErv2_$5qJu_VS>w%Dno_Py|!>K+`*y8)DOmE_$mH678y4c&9n@{&$tfoT;Zp!)bpX z_Sy7#p?1I_Aze)tQ)31%D)J>KUBj>7wDyn=X3E=8k-zEtT_3}~Ya8d(|9#XWm!Btr zQNZ1bpCEJ~d7#w?s&?AD>v2xCL`3wwVhj5on80oaGEsmay;}IEXwy#x>jQSd>8Fffy9+;cr zG6X`kfVvgYxp%<-7wVOiFm>;$x^uk1tgb-m!q|mjA#W9t$j&j;)LuLY&v>6P;yt4Af)7~TsA4n!*-+(T{7^MD6my`cXtPxxPb*>w$$;;%jtx+#g3L`NTh(g zZ~E^%Z}1Em(Toy@`S(|oh@DAj!rpg5-Y``^dVuNCmoh+373%BIKZy$>c0m=&`hNX}=%`-Pm58y#ex4=nHF_q;orz{4vO zycQ3AK=GdJJ3BNqT>dFr@G;_&k`r8xKsO;uQP6}@84gx$JNcTMceXRhRg(9Zr;JLe zppQ^pIOJa(fK8!^L<>)TB9L#5VBO~s^D=LBZa$y(zLrmtkC{lZxy97^Z)2s`T}pp`CmW@6f{k^5qpMrDa!pFY~RHQ5n5e8z;I;e;mu6HDOG4SQT7! z*`uJWS$;*BBbM!aW%lF84vY_LnIZZ-BFIGHEo!9T z4vd^`IrTyI*=Wn9;hOA-wxBYo=9*D~JX#6P4l|hFMpZySU`px90alyW&Pe1S@Bs$V z;$|aetX5k8QRP9P^4$z@m8~$Hiw3$cF?V|X%K#FmJ^j}@u1SL<=lsyM6_b>ewXUO| zRBG;0c+okS<8%f*l;>JxCJh_VLI3|Lp0JDzJ=2nfbW|-WxWf#DsIt*uzzcwKUA&ww ztYFgNmTl60p$Ou}=BmZ?Uc=K!Y`@+Yf(-v)bLqY_Gl$C#Nl)}fs?NBms181K)qY#) zY>U03YHaH}uo?6QUA)J{Ig?7Ysn#0omvoPp={EMwQi*VrIy~LnM!~4#dxv7|>t<$W zJ9#~CgCD#KHxZa0bpj;-QnNNRvK?1@Q+6o@0l!Cy!2|}N@)K9P_ATV z52#tu8f{@Tud>nuvWw;4;cSBtZe?vX4KaiwS#U}v4j7v4o*u^8!r@$~twkB`DF%DhcNxDC1#zZJjbUaK zVf+Kdj~5uOwhQ)@@6zq8w*~>C$a3g>hoQK7?f{g9){gKzn%pn;J*)>g681`(lCjJo zVZiWAWpMS)pXZYSc14-r@83S4M^K$|e&iGow?|NN@yxfMw@nr@0bf4RwvWip)~G~5Ti9U zz~pS+@6szbnXyVwPX~JP8|PzUYmawF`opQeWUCtPitDeJo;X(hbo(%7e@KUmCPvO0 zi9h<`3VHDj_2hs61G_?yKPJY{?@JE`4mO20PQ+xPF0Qb!U)xMsM!|n)zit6ARs1&O<33+W29k( zsPHdX(?(OO3h`)talJjytGe$@Fxzar(>tC_F#mvz?6F|B6-d>n(Q7MRb1*pv$aKnN ztg!jL4Ofe8;C?@%(-P=tO{ZkarF~o}-4pho@uYi`lXQM=<4Z5u^y4|lC7~&$uorEZ z+v=JC;xI2ouk?h8Jq|-uBZ(wy^l46@zk~#-N5VjTwAi|I*85IU;+(d3co8qm#!sK- z7Us{YqekCov6arJPIq|PmrvdYq`#kEvhDTRGYt*WH=susqwdDy1%sq_?H~5_E1|_s zEY0GDHdRC9-I2QoyYr<3TqHIUXq8du$%V~UJ#PeW^ElLp*eQ)2B=EBVCB14JiZa)j zE&R3tvCHF34oH_~tBu8_{1Bnvys0rZB9`q8cGz_yHCS{?a+>h)mcm^g9lYebjJ=h&2E7LRS}cO;A==x7~k*97xNv zoIOvaB~PAMJ-sJ5LeO}xbX01{wmrwzuq? zaP)C6nRQ5=dXwA3Hb~+)p6;y)MPip#Il!foTa~(y)lXkWbIjFmFZX~YBKmr{-GP#u zT5W)Nv`ZhbT=>L%3?Ia=9o6i`2Gxc_bc}X3zMw5}PS^|a=+srmHdZf~2xw|@kJ6oC zVfp%1Mh17GR2ybzr@W`XKfi1tR=D%8gXIKypcoEY83_9Aw-)rCb%JT$^9v5E5$jvu)m*l&M?YN zU}{8#(nj=3Bta>rKk%0C%ea$DGM1aB{6`f^`6yk>($e*HnV=4MIJh+nO?>-BTm7g5 zr;Edm?16-Qa$yk>oufuqtIdfkSA)}+Jm6*8d-x_){=JqG{6YdknzI+;;R|oIZ~l_W zf+HEu_J@g1Y`{!vYhp@`h>Wg$)~-&@jr=>WU@z3y#sSFjj7*+DJ%yN%(a>O0J>Xl` z+bdq-vMbaYW!Wo&g2F zz~%idaDYmr%&BM2SiEgQmi>sDG2-8&V`N(*I%Z=o=Uwbs;L`bC)^Z3j-7B4RktcCu zEGq=EMTBn^Z`<{(}@)u8Q=SQD@9yO>&pw_7&if-sbbkn3Nnh0BY%9NtgLUK zw23>#!~)Js#Uawue!3xQ(6~LiWLk6}C~10hiaNmi=?vZIrpV#?9E;U0S$ev=bFBtA zPBFi0cfubr)Ull7fdieRkde&K%6g_&8>@rQ#~^-kSKZp;CvguB8_E>nORxN@{3*F) zgfwXm2-FZfelizyfPMUU0EWjLuyjx?*Q3BPNZYmqT1cVnnAb>0qLt+az#oVet49*Wk;Q&3EQJtMx{fo ztvsL`vmTKt1+ql}yX0H3IR5{pQj#|RVjujc)rQMSyP{$e50ID)#I>y~OPC>IJt_)^ zs^uWR|Ku~6Ld_PY>m140rRp3#@$2*>{oe%aB6>RzYESG&Wm)gbywiTL&vU=*xY-I_e}uu+ zSZZ`#B!~0Zd%B<+#9<85?TbNKJ{po1=imlLSW&L%RBHWytOxVTOcpqwh8vujZs=LQ zSV%rPb8~YSy@Y?xDzD4Xw@>YI_(O3A0422GHBD=4>sOBz;p7k-Zf=gaI7M$iKX5Bw zJHsMXeU^y_#v^{Kt6JEMR~0pcfpN{LpFtJU-{1c@TZV}N^v{9UP9Qqf=9)N1i&?ql z-9feWPF74W3x0gi*p5cmWzqTegx|VYl6d?S4jS$+cdi#$lSSVcDH~-Q7*)xJAj89w z!{1*bytYT?;kFk~SK=_ql(ONPvV8>wMa=9njS_2dB<*lb9oVSW?|riNLIO2QYgPtN zitN<~QKIkP6Zf)3R!ZHU>SycL*LAX0oGDNUMRL(hx%Hr_N$Z)2lE zIY1ZLu3Erz)=y})F0SUr(bB?wNFr~GgOjx2eEBCL!Rv6Q#HH#zC`w8iVKZ9o`{jaV z0s6Nsf703%0O${VIP>NuWlUiGVRGjH)6x$S5s@g{Ky>%EK&tQRIbBK2dA4Hec5A^* zPE9>|dzX0gwhX50bP6Bs(f%??#AAO6TUaW znJ>uA{W5{k?CY=z;q*|66P1D_H6}dt-QAi^twOyW1qlfWSUF5R#shtQYs)QOg-d^b zrKf*+2L}i2{8E6;#pdx!n-#79T6!fCYTf}ub>}T^R5`v4*BJW+)iL}x%N+HZ7-!!I z2PnMw&Z+}847bJFMSQ{+ASz`@cZDRYAa*E^RX)a{3*f%iUkU~}&LG=iZkgFeuk4FN%g#@>PV*qE!F-rc(0;KFTqE^6 zgFao%KAs2?IPO#s-<*SMEWXRcTbYm1`{ZveBbtPEhFg0Psx9$|e9d_dxUy;?w3yIOXF5$tQU z$7DX!A6=ACYcSq%(PS2-uSU!oKxCZ`(c^Pm5apAFl=p7O4~oZeT6dk+$Ax{z@oVFb z8BF^A3F|MozEznn4UW&5y%ZIYw-tx=65``@ZF8uI?HZZjoDK^QZ=DKk)M||af+aR2 z_6x4)4bB&*#Z&ab=8cN2`!nT&yMNjtwVz@KHc7j#*CWJhF5``NPY$tlPPEIf#R3U_ zF#NH3EXqtqQ#txYTU%Q@pd}a93pzSIn10^aH^FV^J7er!e|n_;g?}md!{TuuU;GIa zp@Wxh<%{`bYY``Y?{Z=zu{&KAoj#RGEg=?x`ZF;a)Vut8sF09+(jOfHrTR-|pmRo= z@1{PzT{k#cr2LjXVeezjaWYf@&!3J~V0W`^-AlsZnnW1!8}$FQXyc}9ED5lf%zli~ zgNhV})ga46<2nkDOm7|A(6VJ5qystSyK zG@oqm-Itxee~_VsZwjlpD32X+#(aU86S3rWm$boIyj)hQ4bT}s#uz?|a?4+khEDuw z3Ctd7e6c-WOGoCWGMHfA*wOww0)R3XZTf<-n1CEpD?3QUV}&X6ET4cgRBUszlSo9q zaw%y;?djv*&eU!8&IVY&S8u3$*?xerU!7bTT=p$bUJPAI=$n(L9|qRDOl)@ixi>_#tIz<7Rb0LV>$qoQQ7NYkOR-J9?BA*G{^JV&QG<)EAh}TZ{dXNVIPe zsb9SbC*tUnj;ZD=`e@O8^GIWMcO}OEa3xwSCO3jqvbH9#wiXdJ*JJ-W%w_Q7VZ$B| zYwUu18lUmj$u-@R_5gm=_SkdtzHbLD*JF)pv+7Q*X?L9^=8cza9cWOwvy#^j_}x&F|+&gSrkiOQ=4Ci6o3V{qMUb=@kF zAI~Pn+m+GbjJv?4yi!~(WNt^3~A_*vYuUZ+}>_uDkTLu7^^^1Mk zNC^`Pts~!quXcxAB3ye|C9*6Yk~3k3c~@ENuV`iS!J)WPzwBf5nSdapx3W4rJ&}UO z;$%brY3sptx|U$K(p(w|OHvj!C&#*QJC!VT`(41RH(o+ocZ@m*LjXk#s!E`MpO}b! zFOqHKy3#sI`(du~0+)!5Euo?Q3i-pbdREH5dFgW{QUGtDTcdpD+HOaxl_0%Fv;Fn{ zY-NHEToXu!g8-K+6cLBD8&9L)UZPaHvU=mxowG9*`nB^R9^~r!?EBFbb2-0Hx$dJ$ z@@+X(gt==x469FiB(}~J2ipkga)p!%c3Vd|p0&a`$d^x;;4mIeT{_#hPA=M*=-4vI z%om0c2w1;;V`Noj@MA$CgCQr4EOPgY`ve2~Horp7a&_r}RSi;4750tHaWmRR?amBY zTm{)zOLf3q-(7In)cmA_i%3{+p>^NB{^YQTohDi@TygZ5wey>d)YM#7$2b;@`k?d( zJP#r320Hk6h}ktY{*A7;Ww%F77}HCtv}uKfk--T}G`pT7z=9%Sd3o6hZ!QTTEo1M{ zBU}o%EcfHEg?Lo{$$1LZFqH)~I}}VgxjqXH!o#%g(NJr6$<0xRwZT*|&Ktg>X?6Kw zn*p5W;R}8pwQ=mjl6KPRi?sEj}PFQ7sQXQkbkp_CYXY+YFT^}zJ=@L=~Itg zbZULrEhZ_tW_TwWM~MF1VrljJIKk!QbSqiXLcy@6b>H5zm~y*2Qg0rGr(UkmU!xN5 zEK`}DN=zD%t|5VdAp^yq5q60)YgI#?0&J9b!`+osRDw?Y%{7IKFSJ2Zq+zB`ccMbDu)m^v_M)cw>HHJLn!BnH#E#G(7p=3$#Y)& zoR8}sZ&D>y6nHEYeh#Q7qCa#RQ<-RNdresjWua$!pX;7~d+;wM#%Qe@{w9}EI39w? z%t)@Ebz(w^`_ZT6W0zwLeER+t-pndA{V!kR)ii+5zaCfyM19z494_x^p&cjNe963E)5pwJ1+gHm}9u z4D=E13i(#i7}uTdGg_+mG!VeR_Z7+5uWXCXPEo3cc!yYfMb-J5{J}0-BkS0LMyP&d z-dZV^Nnv@mJm{$Gmgc4cqfP)}{b4Jn%+7@9_U6~@COswYSlHlu+in31(+w|Wstp1t z521S?M6?xO3|%Gu#c0S#PCryjCow=)o<>=icb^+;!7a*$W3O+wK`Wvu!QAVG zmm)bd7Y$vX-fu6uPu-EGZE6BFCTfxFvhe?h{*M~pe-+Z8S<$) z_wmite3xuw_KxW6fnBuQ75i&D)Fwvuo8<=kosU@-XTjBuP-tj@e2>RUY*UnvM=My0 zxymGz2&q%3V>Q}3!wv$fnw(bc_WaQE1C=g72K24&z%5AgNEBuwb&%If`9q64*4w`$?(m z3^vH0G#-k3MDq&jn75+7;C!=G@6p1{4AFjVKJ>_hluvcsS(!9a<=VZ6^~QS0^mp$1 z6n4$^#d>>Xl=Xd!p%;Du)4Bz8%N2Cjn{y>B?(4*lN@wVO^7q{yV|Qy7%%E9| zzSgNWa%>!vot8_QtSHH@h;pd$E8iPCcOBynI1f>PFI?pTI%uYdi z*zG4J$=8PRc_iMhDr`1e)U;;?J)|h*bba1-+A`fIu{Coq!m!)^D^MRJDkQBbsd~Cd z+6o&9doGwE-F*rW0!>N)(#^fVoS(mivXynL`Dtcn*KldNJHsEupTYJwHFq%q&A!v; zZF!G3ws&$Pf*#W+e=qVb`md(r7iI4NFQ=rPu~+cCQ=% z9XQ)&4YOS};{7~$UXKae|ga1izHEToof#hA*he^xh=N2B!aqvCc2`=C9@?0v; z#=SWyiz}6?_9Z!5qGi@s*}U~c{sG>k{^8=pcWiJ~m-0kuBGdL!j5IRY-%nDPZ?D(_ zfAb5N)kB=|nt#=z69}procXK*HJ7agN3s=S3H8kuF!S{E^hKw~4t7BLsR2_vK0pUu z&aD-5o=i_MVdLJA01~uFuZaJLpr*42+SapE8f=%qEmyt;hv4g|jezP5TWFTJOL~kk z<2>$yyf~2RGI2O?-UT`=xC?N~)gTWRx14(;-6REuz3i)@9U){Fx@Y-S9@hVD^o^JR z$le*e78B_da0QBA=g%C!p^c*b2(xv;-ooMrhfkWxKtwNcWw%4$8TI=Gsr?$?S4v{y z%0;I4e!e#G2?iw&Ldg4j{i`ePa_9l`^J?fK#DG+z&FzG|d8YfrJ24Ms`sb$W4x8oT z@Z?TAn)_j|$F=RpL45|R)_XQst+e5AOKjUA){^!!`c>VPWK9{~*f40&cEH?P{>sS~ z&M)MjIgJJMOl#^5enf50cUPkcs?h=a$gwP8A~GPeD= z=zkPuKVS&J5IFx-%ML`qT4~LuoG(`Cp>L7*v0SSAb)5UIul6Gda0VckX8Do7H~Lo% zbu>TasXQ*?GFxoB0L{=H8!t#qetrk(eiO!Zmifx!k4haXM!iPW8#UbHECj_XK6yuR z@#+&SsQS7NL6_?kiMIj41C7U;XXkux35~Uv`lV2Tntl z1CWk*J@T#SVm80anGgFNc{6r1s#K7G>cv3ZYJUlD_Q|u~+AR^u-ojDCvHF;_MLC{} zM~_#?-5!(dFu|hBvITBNASty+DE%GMa@oX(3pEDNBAbFv zR%?M}rVi2vr+z90!E*Sd6v`+a95<LQATux8pD$7fEXWseaoUE|r(~a%)x`a^d1dlZsTytv^ zL`ezm(sXo3(M?^tq#@yB(yeQaT;xshy3sc&y@nsp!l zwOYuN5v?J#Zp`wk&eL?s{Q01W{jLTo?c?YdT21~Z7}amoqi2bgH*ySzbAUaOCAR%e z^D3_FE+7bXrZtuQF}n=mBk3L@f2K6vY_-)1u7qb2bv_jwB&3hS;c`ISDX)hP(SVw! zMed9PO`T`2FYj~m&A!~-i|Gq^#>q`

Vbd3ytzm-Qrl+BVh)t?z0#J+`pa54+{qe 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%0DTSW!6pA>%z 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�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 IM6N<$f)IyE(EtDd literal 0 HcmV?d00001 diff --git a/docs/howto/manage-icons-create-3.png b/docs/howto/manage-icons-create-3.png new file mode 100644 index 0000000000000000000000000000000000000000..5411552ed147b2a8f11f52df5aaaf244333d9a4b GIT binary patch literal 200834 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

K~3sYAA^GlXYW3pbbF{L@9_G@v_iNyo>XPxkd$l z3hvgd+FqmPC~^!QU*a3CnnWhnyMs>P7S)o-B_?CdT~m!Ad`|R~9Rk+G4Q9FFz%t|c z#l@YY?lJ5T6bTG{|I!Gh<9d$%-`VsG)xN#x?&P4Qwc0!nh+<$iwieVOS9#}mtt zemS^gltqe~ny5``ODASE)L5tLEMLDa{;|ZwWw*&SjU`w(X<&cBFEB1xIB{8FxHD*s zcHmx?r!yDsFQ!zj-!egtQ&(^;6@wTF><@ng3&%vM69+L$EUU%U-theQ#(<`Dxe<&* zt)m|02Phi#%!nSb8N(@3gc~OhdiK*dr{4;N0DA{kLPt1D4mWE0jIn5`BQF~#)9Lkn zdz~uD2*{LTol%ATL{SLcVMKLF!eN@$%vYIv}9Mt8*X0 zc)U3QQ{?&smXTv2e-2t=%pWDfGhdLDZqD)(=%v65?M3Ao9xc3KFkwmHJihzNI)ph$Dx5@uF+rpN7Hm`uHWHy*PS1N<1 zE?BYt4=a@_p2@#KV#8oFP)1&rf9_q}!$Rr*9E&qPYdMp9CyttqRoM!g$0^DJFLfB> zY+E+0IuhEifdVUB2(7DY8;^+vJbVu7g~60;2&PT|r?-`y0GZgSC{pjW7Oq+Dr03Tr znUMp#=k0O#WOPFfIc$Y09cNDslVF%mjxzp4ghxP7LEzdg2ul)qEF_WbQ;3_Y6JkrH z<~OTT(f{@oC8^7qNlUrT-%}&m{|3hO#5jI)7YnlB_i!>wir;EK2P;=H?w|Ql(S9THj2X>P7uA2K_$jIk zuc9|;mbCHrV{ACGaD<002zm~`A{hikBcc4hA)U^PH!Wph@p%i~Eor2B@LOq``hDus#H9#?eP^rjyKp!qTKVz2=Xr%?=j!?ZB zeTm^&E^KdHK7s!y5cWb&&WY#3I~AI>R^#>12#?K@d~k3;I(a`=g{h@OPkJs&Ix|F= zqiRouEr5s#7W`!8V&&ACG$Uscc=+GiInBxbF^+q^_iI|D&x$jgxwM;ujivo&0E@&*qw*wodG|8n|DO6`XHK6G&^-8-7q#+I$ zuJ2Fr@%lag=W)Y(>S!mU_fUZ+#&ceCzY{2*SRR2Z9BP)_ME(tfLZ|$J_kL1HCb4eO z>c`zd|07%3Vp=Otd7(5bnQC8OU&wRq^6}38Za_eM{dF~GL#h=~fid~<*o3jI4}%D~ zh}#Q)DWFiJE6LBT4j6uRpouIi2EssC8b8^72Q{!lXLf(FYVy%(eLK~|bHxMPBElB) z*SHq5iE>ycmt={|Xq?RhN4UX4!es|dWT*EjGXS6Qy;1n|!CrtqJ9kk2(uk!Wt4Cow zxcQ3nA~8)pxh@#n{ICtQYw;W1KVXtzXKxP`1nSR=On%hb3mpVH$$+buPXLdT-*Jfq>|ky5k7vk5y%JtlU1i=3|ImZqe{+yFNj%sRFQ z?WRn_x;ebaMub4f-?nY{HhU{8oZy>rDxQ&9_`(B4`^>BHirL((Ah!as(=O{z}f9$t5d z?6zFQyVWS~ZNwMYJVSv++BKuAza#y4JqkIsg44xtzb5?r&&$4wetlZpPv!D~d;5@9 zC~#@CN)71)j$J+NvO$t&S7HdhdZtoN=(&V8;!O58?v`>-CZ~A9#5B^$-{gEf=yUzI zT*N55k8*zs<&-~=XJ7xzHCPck90cvI#G258&bgHdLZi@~+Sq@1c=)ftZe5 zv~CprhYG>Wv{L5V=!W3UXV;j4dK?u1P)TpEH&wCHiMxTAYdNDRo$I%Nn}a zQr&x-5=y5JV?Bv*<1X>w_1>l+3caoS_O_UcMi2+d%KQt!T;P9T9zrCk=V55X?PY56 z_?Mu~dix0w;!Ha0z>^B2d*1C`tUaAw^`yJIwXgl;3-RJ7^Z^rM@w)sxyiYAOz+yjk z@m%Q3Qjj!w82btRTA5>mO3_qf%E{E2Qmh*Di7nQ|R`VnF6mh?B#d zL>OR|dvBV;3k{ycbc@d`xE#j9{_5^{DFBKev|sXqn@#)8+|LnJ_cAI5x*uBO7{eoy zy$+J%^HF=y`VGq`fPXw1E@tS+i6|VzgbieJBDMq0Wb)T-3lT&xtJaCerh~}Ek=|R& z?HO2>hu-cC2)25?-v-tL3+nLD9GHY*z6S?JGw8JF*DWs1Va#>c3VrLZ#y^x~lIgEr zD^aJ}pf`X@Qay=-@FlE0{f3=mv@yCWeH{A+H?mF|$ zT-!lfB6&m%BCf}M9~te?9y$$>C&41z|{<9%v`(ts%d9$U;?j`ef#Wv`a zlr0C2A*oFFpQ2yNR};M3wz~%}bWDtDn=y+=Bs^gt#7Ps&1BhwuOf=|~gZCyXM^8uq z9j`S9uJE-Ut`_OJg)x1f$|3LUuPtYH&(>3ODt_v)`Jf~N{^y?f=Z`#!ytUb+M2|}o zi$4c9f@mvDK_Cid=-?`QO3C)t*3LNgrdgr{u-_)*$p?^wkb_`R=stgBHU9N@D6qi1 zNgQtj%r#vMp2@e*h5M0Rjd)`wGN7RX-v>!d+iQMI3Z&{l@Qk+%#?_Pej@9a@-}k`7 zwLG;2!a|Dy{nh__k`&XTZ*TzBdUtHN-mztpbmP&UpXuZ<$f>8YO>0FAvN6aWv*yw& zs6nte<|ZWQmEp z>I?vM0j7W1L&5m$&gSg7%B{p;;lLA@|{XgOg!r3C9MOo3LFctD66I z_yZOgZLJ^`i3C3W#WG~|(k?Wgi)@w$HZD&&AISq=g^ZxbMw=P6H)O+v%q(v%!Lf>*^rrRJCu9?PV=$?T`CA2u$Na48-YkvbW~evX&U5dBos=3;Kq)WeVz!d3W5#RFB1K;kx%z? zU@Qfj(u>4!JvC4+)_tWfz8Dn<20=h1mFm!WzFG6@UVhcOlDxnsP-J;>IIdze=&5Q! zBd0(OQr$3`;HbKK9wDr?G9p+jW_`?ZpypWNO#GPXch^qi5xYFV*X`v{W#M< z6DxfWO4O&NC-LbQ0|p;v1^UE-!9+8Z7t3$*9-~yx%+nu1-x8`e|AZ23xAP6b6MCb} z-^vW=HpjLv&)2jI8epP*5j-ls_g6FZ6rZN5KxX;_~Eecg7jVp_uO+y1* zx!psrD>FA2lgSKT_z$Syu)?{L!gIQ(j6EGMZu`)T$CdBXjCHu5nzqCmu8v2 z*^snZvyu)bbPd5nYIrwKwnqO=*qASBPscj1vNEu>Ssb~yJ!Wrmu)H@`sCCBw|A$yR z4j4g`_OTm@%~#cnfz8;W1fkZws3zsQ{S20z?N0K>1@XT`&xpMb(N@1bt@b_cts^5w z3mBxBnC%8tcU_i+0Keg)vKqVPdlWc&orSup5}ugb;@(rO?Q!_I8ikjemKlyRf`ZCaL5p8SHG* zR(9_-rE4gg+T-4i$gwD7kcv?VulzYjWCj-WLG??ph)-^dF|t!!7Y%h`U*R_W-=e-u z>GHhS;2auV%%5-LZ6pH0xGSg~J1RUDyZ_ip!TOwZ}pk}n*{z81s zS&H1P*r?>OG2JLJ#lL?|%G~?)bY^{fM*E4=xojq@T!7~2is2CD;~kF39pP*5gy|Yd zi8PO+UZWXiD=mEO({+zKXAbS&XB&^x&&n8a3@3%uQhM$wd zV`6Ob0_k=lDYH!G-S6x~1WfB9WRu+>=d~SW`s5%U4)S7?S&SuHUo#hG_2M2oeVvjm zclT`J+&tWBS#eMzGLvK4=k=}lpbd_gVYFO)qk?8`Po&RiyE={dUh#3yZP)#5UtmZ1 zvyEFwMd1^VQLR|BzEqw=1%X{forO{eE|rmXtyu^U|;7Y;w~+IhTPx2pC-3w zpcTEPytz6|=Lq@MMP<+m7_Vhz?Mlu6czsAWQ|3*Mv@rz@)e+$RFZg7X9ZrqO{{q%nLlxVg&Vx?Dl}hk{zvZ&?kQxo z!okA9N(~|Ks`}W1;kNiM`XnK@yag7)ZsFJsPvM?Evi6^(wDY$en90ip5RhfMsb~8f z+3dK_tl#ymvUyK5)7^w&-S-V`Khu@p_Vo!N?zffz=e6~75M=szeTQt23l7#}8ySO7 zo)A8G^=`s1)>LX&^e5*g7Axvg7n&VB=SNbu?Sr?%`yY4!UJaGywV~V2NVvXa8(6h_ z?`jk89v?9Isn*9Wt7r7PVc7W69%cUTfA#8Fbr1p&N08uawS?NveALcl3FM^@z*bKl zvaY*h`u9rNyqljN;*AcEgx%Q`X5OhYHvNQIDHxbfrP?+S8+>M7mW;vHY*e zKqy(p%@N1N)4JUDs;dAgJ9}aTEMY`@i^Zd@0!Pv3=50q*T2OZgA=M# zj@SKUVXkJazp9@F7KZPfi;ZwRW?3(V%VG3b5j?Yu?4P%s(g!ml=smv)KU+Yh+FZS zHFP@93|SegvCKNwhG^e8KmA|RS434bB>i$#{_HP59+N&@)IV-T^q)5(s$FRWwgBEO;Vyb6skuQ)Wj0njznc9TXSuXY=?}IfYg|e5>@* zi|Ll+EqD{Fm;20LVwbb(w&SmX!f{h^w8VJdu}3)41y+5+aoncJAvtuP$6#9{@bn&I1Rom=LFH*f5>7z77Y z7(t==s0pbgwye6BrmKFejEl9OtZ}%6via-FXP1jbjF-*|#IZdf;J04J5Z=dY@ob%rPu3ruwG311P zyiZeKhcXm#QN>V`O&PVU>Wmpg6VlM2PSSJ1UewUIX;5O>y)6by){roP@^f_5hjXV* z4XN!Gtb~_w^kwBcom7tb$L`!V zyp>a73_xT6BnM{-@ZZ7|W%+nGN`G6-Y+q=7Mh#6zItj?D4XTfhsxzs z(>9gs<~ojgG#BRSB^pHwJ&GC$hlU+(KxR62P#9`_380aMWos;Rik!=?`P( zCic)47*BtM9#3ET3&l*@WPc0898XkVOv)bT$*LL%&|nrs-^=w(cu9H(N7kQ2A>nz2rz6K zLxbUb_i`g;-?NA#89O-p^omjL%FKD`iiBFHtY?c3UjJ=8pjhft zY41z-uUXm^afB9{b3$eM8Et$?Mmx>k3dAgaJVuCb6dVWT$b`>i_q41_HO^-J`f63K zoC|=R&|=hO-N_)06zu2Car1?fTd4U)y48|NlN%E@Il5_F+~d#y=j35_839Bw^uiJ* z05?*;f1$a*%Q<61aN;9UpSh_%e>!QqTT2?vxPyi3d{(sJgtu2dCbb50jPZJY&SC!5 zBRa!47QR9=eOW9u>?TTn#!6$IOGV9+)gE6gjq)Td&44?e{4W z22L#8_>MA1ZtYh%6A>Z0AqK21r}}bmIqg7dxV5kPL`0!eY;Of{W^P)wkp}miAwAQi z$y9mkZ5u0mMG!9W13;4!-PKM5br$#nA5!*&T zkiCQc-XEf|4rvqBKbn-HvHcUwWo(8dEl67+{d}GL8Mwr#r?#?^dwoO@A#ZFcDsT;G z9HfP@m1UUK&iDNQvZSJ(D6$gaX7_>RNER4H8_os(?cGhV4(%Bvj6!?w>kq(3_jaodWtqZru>bWS^?6TS?- z`XRq3Gu|t{aEj8_$L`oCA^RUN9c^j=oEEeXBdpE)nVzYW?5w$YeCQw$DrVN?W?w`x z6{fp;GZ}e-)9?aFlKOV1umFsrIX##-N%Q(RJ#qC!f|seN+@cP!)6bddU#;m6M( zI@kMlBT6$qp(0Z#%l9c_4fylnhJ+ZGXvRi-EBpx|2>A_&nq&72^S@E%GFB-#R*JZhFm=k&K-o%$X)D-!1F+8JmYWqTKf zhXy0p2tsxRBJs`Ss~glU{ym+Fys=c>c4?K6kg;{zz(#~POHN++*;rLtPA0&JiJqTd zV3d+^loO*EadR}Mj|HXkl_e~~0smaa%q~@42bAs73lZpC@b8|T3do@zniw&9X<@r2 zWUAgQ5lgh#Y1O1tWzEFSALyW|HoN&SpTAfV|mY^AbYRu8KR9MFrgHw{&@zlUV z?Gu{sgmxP(f6?{Bp?beNFeCoKEhxX4JR7;d@$6N)4K5H2#<=KNabI!RlCgU8f}_4G zLc2*U6B>WQ zr$u6S1Xe?fJ!>ng`L2g^O}A4nfUnlYgBK8I#|{2Q!~`Py`6d5U`6o~ZQq75=%_j^Ow}mB`B%O2S08|_m)Dd z@3>T>?1StPdGI~owyAw@VwHVfLq;dixe-!p#no>aJSQGAu!PJ|R8&;pn=D|ID0I3D z?6<|=xMZS8gwahi?Qxo$J>9P=8r?5;9{{^edXDv#A6&gm;t~?%Y-|;aa^1^tU{(NV zcMpz-mBwf0*=JpB_jDKKS$x_nLlzvBot7=4TYjvM)gzr+&CKR!FPN%5lx z!bDFi1i)G*N<7DARTsUPl_1o`l%lfPVQi-u#^s zmi9hJaCV?Vo~C+@rDE>OmoZG*)U%`w4v0(;+qQ-bM5s`WuP^h_{8?drJ#5xQp9xr= z<|hi7KxcfKZD#LDW7go`UxPEH9H~$MW0e?sq5h6=&>Jbn(z&28KVKNA=C@Ibm1=5n zEym5-H-a!Sp&^!|KX{YY{rBu5_+nUEctV=evT_}|V1{)c1>z@YMNtrQNFhNn8qI$7 zC`L8ur=%`#*5%Tix6p`#9o|n1?Ut0lO#j}iW8?XhthTmJLR$LRY2JV2WPl02GSh<` zGrHZczP`R1ft3m-y2jO(;H>?PpK&xLbp8lo(dh5~)h5S_f9N>=57?4IbF7eeC zUb%eXED2u@wxP(C2nzU2Y~H%BZ$VsI84xC?y*$yLKhQbpY`QE1QYbHew5$o(4(Vr~ z--866u|vNjo6mcGPdZnyn*_^f5B3H$C1mBQsR3w7?>c(!N}u){mI7rIicydjrPuZ6 zV+c|C%b79YC<)wRe2m_Ga+g`mV_YEoJFliAL(yub>DN}SArt~Z4|OLmLehu~(4=x{ zHC}>;2_x#ydWu*!8_~_y%rzG)9!C|# zNfno>3fk&_8`A0HlM+I9<9}9Sr;ITeH75%eLv!V~z%Hx#l7C62_GE}yX54JdS?`L85d!8krp&7B+b$*z%DDo=a zxJH&S|878gkYaBap*U+;k;?_Fp`jY!27I|_dd6(nC;oViEe(t~`LmF+CVFf*N-8T+ zX#T-s_}|UWVgNp=VKQmAKz8o_rVCGTX=#rQX3bVH(l`3wz&g5=8l6%xOr0zZ9Zd%| zEob#7PH9D<6fUrMpuKJ2#*cSEoVmgQVvSJp*U-1xw9#w@JHn&c`_+)Gsu+^kwzW zgOEyBdv2v^u!S)%*d+EjNe;K3HtbgWjHhJwS8YQoDNYZN>zAD%F73AI4&Nd zvgHkCJXG(!pb*72UM_cWA3EQjFp+refWr)E;Uw3L(E5Ewuc|py64OlDEZhhkLtKRH zc?3Q<#bOps&THlETVqeVoX^bOdhrGF9J03|K@ABQn~T-))320Ly96vu7fPzSef>$g z7s#XCf=F9;r3{lPo8+=jQ`-FAc;|{lbHd; z<{Jt+-D&Gt2Zhwb3%^5yjY(H_x6|1a?QyIkn^8o>$ZnxpmXFn1Q|5D9dwzalaDx7? zyG^St#p5Q$-xntn{on#P6`uJ*&zHrv*GEdglz`;eCA6MTET!p80D}}J z8rAu+pl|g735A9PMrQs(y6t!JI{zn@Sy>`v<)*w}=c~|hEsY;bOWWZ2Zkv|S{Yo2@ zaNI0RG-#Sw50|S1f`fxI2H1y&h7LURj$^8Jgi%N|tAVsh3Lu$V{}O~Ii1VD3kQn!K z(tdl!=`iuTfq{>De+8BLIf;TOO682OSM!m4qUgmV-b#DupGDVVP212e^q+pk`WG|5 zjaMA2e%~CiVbD*f@Jx(9ot+d(wP*;c-#T6^omah;k* z^#(nls8&90zeBn{TX#wM3;+azOVuV@Yqwtk3=Md5=fL4l1o7Bq`Bp5D2-?3`;E6h5 z-U>K>Eo~EqCvl>}dswzVQI|2EBVrcC2XN#RjYa=)lL~DVr~LwxQ~PYA)RdHdpxNWz zJQ)iGP_AE*h67QD$%Tfq!)HPQXQV{QRx)!DCU>sdHUKaBZ|EgaODIK^`RgSuNcm^8qvXp z{rXtv(^AV#SC!$sH~W-_fCRLn0;z#dGUw2v3ypC>R1@zn>$Hhsu!aAJskkaarfdN{ zuUBk2-U~{fyVeWd#{-ot*V*NXyvP3HR;Agvb7(eDE<`{0{=oFZ2NzeuCGQ&u5!bt@Fl%T__ z#k+I#@@Yfnu*n=d-z$b?9dm1+|9#~iw$TJrQb2)OE&1e>0lgy=HNGTuu94v(s1rLOqWTf&`%&;uu70X(z8@kjC zMF%vL7pjj`9#vR|b%!j6vf3%$Y38(Ldsg45BeT(fuV-k>jtg>&tY?k%dC`u-VorCQ z-~p0>-P3$t9tkWMWrV2V=#yU{3O-TGC#>ydf=+1C7EfGGJDwOaACB~zUT(|O>ko_^ z9DD$GgES!hGg717gLqy~AY2BQ;~YpyjE)?L;Uf_Yo(zy(>(JhE zL-QO_B2jiCvRZ{k_I>lZZu-G2O1TSwB2Is|B^T-tT`z1(HUgI~S{!4)@LSE)f1_wg zZ63opNOi-62|!kr6bY;fc=qDI-++LHgT+~;(57;;WtVvIRA|1%9i3K;Z$uqyL`vK2 zHF0BhVq|Z}X;p~kSt2QrW3jIxxAHu0R6bc*DL<>V;8jp&iU}%__d%0}GuDjaZjSTu zE>$S(=kvXXK%tg5|1H|23;Lx+^pyL#*~pPshxPfMwhp`qK>#o(@FWDs-7gp}g= z9Z)o6(X!E=mfuJ%cDM{9V`$W!P9%Dw)Tn&UVat28fvrz0KCaQ-U|_^-javQm&GY|Z z?Ja}ijJ`g>5ZocqxF^Bg-8Hzo2X}W!aCd^cySoK=@I_>Y$Hm#m8 zX5nltQ5_tiM~7SbzV2JJ5$^ScX#UiDykzrce>K7dMBjMMeUsHHn&&>3-0)yV?}koW zM1~vPcW?I5_fc4V%fK2w1FAJt?927R0m_=yx>zS5S*!B zmLg_RWM`J%uHZWVl#lhCYR8@~W626Gsb2r%ms#;IwhYK0rAx?GN9y_(5ub=jJof+A zTEhlTte8c;1B?Dw9&NdgjMgP|j?8NWR*s)xo=F_N7jGX^zu_qz(2U0T$UL`wvwHsB zrb8CYfOK?X;q*RlE({l-Or_+1&5RN^?dv)0@fLW97N}QRq33hPiSfG6li_K~cmHyT z6DTa93@;c~{C|uwPwJRP3N!i%3M(FK&U((dW&W4^0ZY77D^-Q*f;UYee*U}lgXv5D zGr88y7YVOcJ&Zm5un?pB39d^7TtA+ANojEyxDHKm-v;PJeE}nE25Yj`M4!KJn}w)s zFS7+!$^%fLI6=)>(CKjf?~}fn3vl!F`rQzF;~p^pKMZhg2lo{Nzs<^?GZ)kUrw;Z7 zyZIWW@#tE{>qV$CMKmKAR4=US%ys5a`9;W)q>JP%va|L)d#K2`2^TI#QVf|gt>qFI zl?rKQQx)31fjoawu88%b$O^eGsp95z1{(VB-)ubG_8^P*n%{_S7=CCS+s&<+hCNj) zu0N0Tv=mPNPn8&gNUZf8kBAG|mt@p42<2K>F^*5)AMVd{AA(#YhJXB3O_E(8PCn77 z{-qPu7Q(~(4;1VABx2ciO2XK3gf&O1ln|S-r8jUL6#$SYT~$os%1M9O%Ow?s#HnxV zqc%a3JD;wPd%*dT_ZGgjMgO^G+rC`Y#KX9@$l$zk#_4gD7BQp>AELHA(Bt;HJ$W_HJ$wQIPH6*J7p{0 zJ^W;*<7iEX9G69?{`1?013|J^TY=tE8D2PcU4c2vrD(_MW3{Rg+jdle1Yha4o-`|W zdX>KB-O|M1Yy2TmM3yXdPIcs3$)w4)-A?T7waR*x_Knw*lu%WFWV>TMM)YLAyD9MV*Ebmww(OgxCj2|ao1)w-|J zLQltAD8@8ATNdo|c_Z#eAxTN9*Ui1<12)CP?w5 zW;NQPS?YPJT;JXBNda@=9aR>}*+oqSs04I?$KN1+tqYU+QzrJtH9sy6&k@pIR15eh zqjSYeyqk~rpTm2Ohi8Yl12g9Kjz?+L5d--mTaYeG#OSe!+{M&!yyc2iqn%;!@PXZ+ zaWdQLNQOUpL*)y`{iOFz(^WyVGhtPNU@^b&XbPU36J$7lc+{_da$*&t6e7PHqs3IY z#|dlpV+|%gK`PD=?)4*nb#+^#(&F1`JZM?pV-R#~KJf1sU%va=V4j@&HV9tJd3$-B z;ksq*gE2A{My(ByjpHISQ;q)(B5|RSb_nm)4PN=`B zlEX)(YHC_$OeV$0ThBPhe=LpU&@KE+q{Q%7*)kk}W@@&&laXn8mw4M4l0ZcMhWOto zGY@FLK!keTjK#*Q$m&mil~m~xol(HKkef7{IEM|1I@`MJ=s#3M8^hWer|F0r9|Hn) zw$+HuJ7&o|c{PDbIQS6-mP`ndKs`Kos-)0aMsPZ#m;>>Dhw`#lCl#7YN|#>?jIS27 z*{0b#FcqPtU|s9x;w^*(#BCk|zc1H*D#lF=M&;yc5jc_d=@uVD*M#Z9EtWYs-%UFh z{Afc8*~bA>*ZdmrAHqCcIjp$i5#rfqrZGo$$CY_GI0&uU?hU?^KPXk+;ym74($UVI z(Hhg@S9j3P3#L^YSlf;CY^_+I^EnTdR7PriWgJ-qM17n2%l|$v6A?M-)Z6&kjF02> z_>oEN$DE=)cBaApTO3H6n%JDg%lf>Il>%3q1j6?NWJM@n@%&4_MJ@W`F$j|lFkK?E z2M#MbX7hsCq%NUv@Dc2m1E(XZ#L2oeFvV5`rA{{^+Sv}YS6~K^vedB%l~(40~`6S~+|O}?nZE$1g6=CfwfBJz&`+NuODW zg4M)WjYZO1L8qWrLC7`4D2&Pu#!dmSuY7!b0JE^pDgJ7Qw~$P>$WR`Qvfp zeO_lu9AN%|HUvxH7gEhp(>|+Iz@BxEUiZ5H0q3E;Uh+SPTY0APWX>Wy?Ib5uGeo2m z$p_!hFDAp;1RA#^r3*CA(UR@Y)yBE|A@jS0;+b?&Qlgpz>T%oR|BUbTx5$|N{{Y_} zml*`~Y-ZJ!#V;l`{Uz5ezK<6lwsZ;Zu? zy1MwHqN1;KDtN`_H}36im*;GccPlm&)YN$`EiHEtSFYR$xOeasX^a7ji)vXRo zk>DmBkBsXo*wpFSc~E>Wztug_BBiogWtp7wis_WGe`&ky?|i(-jS3D4*(|Fa-%89C zjY;_vSYftvI^@6y;YNcP>Wv%aGvu7&) z^9+Q~zd07(j1fAl5hKjEKtgYgA!!$>?UnNm!}V1TpiyonSwWp}hvl44w#)QkiToWK zrUQe>!x{oyq4nM~tx*hNV%#QHnxuMxR0mFa91ZAjdXFabNcvg=*XY$%t-aA?A;19S;RP%^ z=gw#j4l~8*DlDU2{o=f_NpBjY<@^Y-?)FS$X0lw$p@@+oy%9=$G?}cG6LDh+0J_u( zw9N4P7gNHoS0p7E?K2oIis3s5H@!;0CHCe4ommV$WejYb{6ZzW4OCQOUv~9H$qA&= zWepI|!W~KMMy+Q zF`d!_E(*02+RI!MbA*d-QcuW!=7`r!;q{~+@(+(0Bw;IEKly511or4E?(hwx|q~KHOrmZUO}vo>(CO?iXN`T_jQ6ZCo3( zmWz43P}YCy92c7sSrJsYkgO1zlr)LPsHChCo0$j?r!YNTEu|=PAozmcLg5Cq{!S4C_Y|KbP9%IC}WHgi*IpQIZX5ITu>9c zGrcH+rNNw5c*>dY@EZkBr@n#LP*Vuvc=- zr&=FaJ?RdU12keAUirr_F|!>3Qv9cj|3JDUFb(_EMBh)nb8^;^s_&jSa{&Z-_B5IK3Z1?y8;qdcOQ*5HpWA^>V@JpU%Mg(5Nk~XW1)I@!3mEjp z9MpKd-U2f~Y(HalM5*T@R5BbC>LbsJ1Qpxc{6vQ9VzT^(NcxJ?(khRUJ{0IFY^|JJ zx(-+#T6IcI({%Q%W7mT5yoV0EENVOZrx|KhW6-+lW!K6w<({QSShS-^E|I26g!u@hI zs|cXk_3Ib#OpCCAyH&G~OaesbQKbpj+A1 ztHr~F5|lIW_k!nqrshJb3H%;%LH=E3tJ_Ip_)(x2F8j}LC>(ohy(FZ|=bI;A;S~jCWs$@RZ9fAM>6B@|&zAFTQt#QB zkKl>|vD^dzd4PiUPvlOFV67B)s%kJXCvnDQV?B9RHW72+@j_xyy`20x=|RX>_f_d{ zueb2rXlMKmZk%4PJXe3qWZYIIxV!~bw{WOGq-2}ESkJP96;<<}>(;9u9Nr-AW#r zIn{Fssj%^R0{4-SwOTNRnO>~V|GRLhn{_gah`E2g-L38Z?YWKKgg~c~?PnBm=0woO z4K$;%W3)&1f8&d^mnI~VWs_jFkwjAsCrkBV>&0fbeL;J{82nQmUi>SfP2PYJh;%6S zf62IV>*a?HRp??$vr#!~Br15(vf zH#*e;Vb@vL<{}|V$`l3`R2YVLq(0WSZ{JXGG06-9P#8{DbcfS896o!!{J-dsMvW7z z|5qK7&{JpMZl9FLJ`z+uqQT8CT_$ovIrdP{ST093l>s*a2DKXhd59`Rp$~J+Yi>4k zfebVu?OW(a-{TfZblJKPii)!t18sbHa}kN@IlaEG25gTQI1GO%TLj*a-huT(%eP!5 z?<@O4&bx5{-eAaSe1q|uNPy@6S5mB4m5b^_@ISZ!*idVXOpk~A%IFvv+3_W07e2Rh zs-`I_&*~9L^4Cigg=q?{6=yYjuhaRG7Vo^Sge?_J2ED;T!}HqS@2~f_!-Q>>+mT!q zQ#C0C)#h_0Lf)!t3x9uuVO-u;y9$ZeB z=MFC4eeTA&q(Ui=du){VGtx|wi1+EUh~$;{A+-^T$YHh$n~b~skGCj;w+u8?wR_G| zKTWcgtQW2^@nXb`YDb1t`4 zwF}EeyEgrx2RZGN2zWe#gta2!hSQADF+qk6Nx>nJR#TV}%|>Ewmx-9_7b8Nw2MlR3 zwonU*sY8n$hQ1PIa=-s{>H!8$JLxVPyZxo_mE2#08#7J_>%%bIwWufBYm=sOWfT9@ zCRE{%bSqF^@-O4wNw9^XLSz%5$0Zk8?CTTI$L4g7i_r1R(~fhk`oS#(6_aCH$kUD= zd}{_<=1`=~lmdLw04IU1gQ+%ia8UsmXGd+5^)@%}-#J=UP492dxU1vGw^c3^v`U-Ksgj?rS?>SV1ks&b(M zEVudxk+jVie+sYe*scnE=gCG7GA`TIq%Y*vj@kF#(wtZQlUexdf7Ks)8;C#I`(HW_ zbg^0ix141#JF$jPRERHVXdxjH-|6Ud!-@Ut^ezF>(nkVbMTfaYZUY4cEh#N6ujOg% zp9}#V9qYm1C?gp%)>K4g`KqL`#j-yt$!D0Dr~@apCYQT3RO)=MT#O{s$ybjFQr^4v zcxQ2m_!4u+3pD1HgAr-lX!!VJfHllt7PfWqkOlCPW!t-`(A^TS#<}5#)XM!;hlzZX ziHxgRv+~H~V9+h2%lb~@eX1P5yUUz=C5|}e+;m_GqApnhH%uWhZ8A%`BAau zE0?Hu`4=4dAGwt$k=J*Kty*Aikry>)d{1Tt6XRXAW?WcT2gnjGp4yV6jS)lx+FsgJ zp2r9%*4%9NT0OmoOe`svdz(7bg_2uOc7j1p@Ly7oxXR{B&Kc7O*!Le${QWR@aLD8GS0E_ z!C7gd(D%x~)$ywF3Ii>1$+uez6g35RcYlV1 zyQZMz4IIPinrh>o?AmoUsMVNap$iBo=Zbi*s}Oo{-!s2zb6dSSD<|tCAcI)6^t>45 zr@bs3;oBUo?Voh&l~bYi4pMZ#;isSk6H|9fgR6XREH}OhhG`(=?QJ@Yv-4;#d%V+> zBAt<7kO4-ZgFjzQ&Ev0v*Z8s@+F(whkCznXMlk??0%nmaIYeQdcY1SA#{Bd5@6;0v zT7GbB7<2pCxyR&@bpjFQKF8*Me${+Hz9Wk050P>+WkAn5oKkOr?e%mBl>6BZZImtN z)}f&>C`W1x_>vN($T1Lzgyf`Yl@?tjdsH2uvlRja+#G|iQ}XiV&RP@<6x=T3wNkGJ zI8mVeSiHFgt8fi%SGg2w@mYYJhyMP22`w6q)n&FHW%6JYr*?I#ioQY}odz=`5#J|# zH4(wbu-AZl=rRO(bF`LPuE`qr=MN>DA(g>Kt3dc&Cdmg{Q+3vY#?U0up+MPx)5XsTvB2 zP+Qtl56!%xIaRKgnkQ?mi5&MYk*RO=iKT#GT=4`@0*x6PE>329KA*B7kjo~1t7}jr z#;*xnyf})>3m9rndXD-V9Z!p?jG|${&*g@Zk{evJTN%dCjj)oDsXvOqAY6o~6Y13x z=fXYEZ@q9i`?@V*{+r^kRHgj0xvaF?yt;|9rpjge)_Dqx#gX(Whxu%g`RfkaeKi1L zx+^*$VKe6Svnt=$>-Pk5Mrq5V1NJG$(qqkT*L$2Z=9Hm=nY>=+ma4bU1>QTTd5O!jR7>)7C6QMJ`vr@Fd&eTGZlQ+#~9yJG6z{{BMhG7Bwj#kdt%Xjs^B z+g)f53M#6+&8r>2y_zSiUDC37onkJB+>Uc&ud$|}>%4{CI34UVlFptuplaHkm$-?z zc@NBZw>e9B`NroOX=8N2?bq312kwP{SZT8??c+J>&P*3oZ?W)MPGKu*qS|;2M~0B4 z#2Gn^NMME;$poL2loUr<8Q?i4D5|^^y1rCa93C7bR6Dj=9pDq%1d|o!wa{N}zQ2u4 z1bOdu!E4fWWL66%no=bc0|}vR?@{+JPx+3nm!an z6dBs7`C8f-cuE)=@f~G{DL8xEa!mr(eP)++Pt(F;Nvf5BZff9fFMIREzawKaBY8Kc zWkyslHc*@NMOKd1JnG%zL6aWVbtnBO`KGP*~_VHBVXTF(W$qFQ5_Aq9GOOyVpaC_h3FJXD!86mZyqR zoT(db!kv>*;PBoOjSGp5RrvB{*;u2WL6;cz?nvEWedPf;l&7!!skMI@lkmR6R2sF% zlzF<*hS2R(+BZu^$UN=rMUpCnRH8r>|37V&N(l#=ng7)6z|M5%1Nt}+Q#gXsoM}~l zZCqb0W;x`?2EkS6s$Gg;ycUic4Yr86-p>#==k3D`>ptaRR_^yT)75wlYE!qcqNS=y zp?r2FLfXDt&>1kLB;<_b25sNfkG|hxBAE#tR`}{DNWoIvU@eAapw`2{BbFg+6&GRG z3SK|rG-^lxvyI=f|AUH;79e~58;PJ2x^YBE_~%a}j;I02;#~itLGd;nWYOH18b)aO zn5lA{${b%WvHKWlBs3-;hnFNzg87|t{rO+&fa%VGw^vTRp(+H`Oo8qkZzk8XPfU;v zS2v?8UZ_Y55Yx8h8!#nxbHW$rbKYkPxmAuqyLk6bX&^F~rFip-S!VdHG3rcWVo~4o z;$**!gEI|QVnA2q$rFcH8`A++P!TGkfbzVlp5*GPU2TSm+$ENuiZW}kXp1vNGKM0? z)ZzB>r_eEY=W!~lC}IKGU!dNTkXX(Yc(dafIgTELcl~qW0M;!Yds7rlD`MtCSeIV! zp`(7S$QNAP1=4}Xe}(V|`Ue7)vpNYQ);{Mla{uP5*#Ng4`Qrpd7@y>BS*v3CLg~)X z)z#JLp(j+^_^UKLPQqnc*>zDATH2{UoBKn3=>95#5i&&XcZ%q=ZOfUFnlP^>dtK#o z%ku@sYj6c`{14_J4$td8sGNzUiBF}z`jZ^tksiSbrFRbFyXg#{e$3h$x3g##dKbEj}CEXi%dD~d|&k(UJ9DXmXUGBT7{@YBXyrFuP=AA^Z=-zUAJp~YHG z`-5ZUlZ#+4i0ob4>iccT)!P^S@g^TI#TNW1cr?3y9_qJ&AkU{qvbNX4;{!K63gp#y z)>f|wO~NWAd4e#K$tVKhb!FDlh#E#RAnKRe!JvLAq>S;kN4H-CyAy~()~8QdrWUC$ zFq3*7P|m}hrf^EM-#wl=ytJh27=KN1>8IOl`(6G{qLh$Y;q}GIF$yc22Qi0HEL-jL zqn|T6!QAk7`&$&v8zh2E6AiCErLrfkiT&cbSbb!1*XmreGhVFa-3J1Gl+H9-aAn6e zI-dF{0I@el%K{R$M|5ij-(ZoOtlj=0Y&`(C#Qa3)@aJI52r$}TDB~!CZ0M*q2q?&X ztoy~+g28PdVy^&)Q45M12wEeMus5)eU=+;_iH~Pi>H{_(Glhf|=7ZZ~zwA=+=}y); z5wcHbbXyJg8Q}{w>)7&OGx)M`%Fl8z)?0Ipz^2+C&{8mN)drKv27o&*IJ)xpqekQy zp9x{dC`p7bS5~{$_N|B@Aqb`lUG@5gUm1x!f1P@ABcCCUMan!L8F+PX(U@eg)v-+# z0aNwO#fvXK47P>Q9n#CA{ys^LHo^1AwzeX1XN0_Kmu(?Y|>&?qW_qAkFnKr zlLKZNV@#QMP<5*8I=pP|s{3gpI4N@^VkNJHMsTK|6d0<1Lr?TF_Or->;!~G{K{yK8 z(}50y;3RTZhrXAYEG;41uL=oe5G?-6pta{q)SRa-voFKZz4R~?qwy#fdOr79PL7f{ z^uyC}WhsrKa=k|LRIZrn%3ZG)0J2nEy`$Y{mr(WuB9?xfn0#*!hV`eis|&@285;Wy zp*a^Lbe*&86(yN(ug?o^C!battV(ZPs5kFa0R@j%pWb%X^pXz1+8f+D8|0XnD1Wc?K=-C18x zsk=z8ZE?qPf-o*lCVMB_*D2B&@*HE~DPXLYnshzU#%=hz6WjTAB6+lY;TjYFItZ1WS z@=cV#moE>Ubcc7gk0Y*)kS{*#uKT=8BjH~gL`oD+4C1=I1dbeCYN(h%fovXMYQZ@p^XE zEduEKXH~TTbHQ@?Q4ErIAZC-tTWApfIV;&=5VbldM^!SAD|7m0{I|&(TsKfm*G%Qp z81|jUi!7Xmg$Z;Rt6w^Wt-ZXHX3Ydl6-?~E-P63MJRea{`bi*j59D!*rTypqea=!W zl#xni3;+TJ!%8bN1Pajr9}y}UQLg2-t^$orl593PDH!P-hp2&4lvd?VYJYg^}3x6*d|zikm;DzJYQ$ z+-FPXN*}LhWVh`l{#D0`C{;i#Aow?5?*-jlrn)2l2_uIggMIE}3q=*gTR}yo0(^U{ zG`?AjM9b?QDfo5C**qy^mscv!G%DkET3u5%Pwa0u?`Q7Q`8>j_t1pcv*|9();YF7& z%p+4}CJw&%83cd&sL|pCjX2n0UenT83ax`8=>Bp3kFyL|tvB$+hU-8BY`s=xeWo`dI2MZB&mYE zh^&AH+=*Fja1EI-#4;unVWWT5a4q41?! zFN%IRaN&%3cj1GbC?~Z)TGKBsfEOfpLO+eOhATGIxtaXjY^z+Ey%6!;B@=j40J$-0 z=_ioUBK>iXmixFz*XVV9UUzkn=vm1ET3!inZyi9#YFrzT+spynj3iD%Svz&4SH{&! zY2{@>NbwQ4Yz#JU+@w-Tf7e{rehUnN>k_L1@u$Y`v{IQ2seqku2jf(ysh}zANsyoLzKHPn_li6OLRB1Z!iLWMvD*#ux1Zek--dt=oaHT;<)62reZJ0 zJyF4-k@~Ym@<0`e1jZ$U|3qOVvU7ZcFll!>p3h2s^nmWK*}|p4>J8MoCZk|)KIx7L zIKV3@Ip&UVjSLTt4SmLsRZy7nLC9LwkuUs&eG$Mkj4d_+dhnMRuf+HdfN0S)J-M4J$)K{~TVp3Ca8P)l4 zaPztK6|-lbJ}m*y8{*mnB&2~!Qm`6e^x(E7LJZTjXr`o0ZK4&v-P0Kgd@ieZl~||@ z?fs|a<$ro7$S@K62@5*=-SDJW8}%F0#fq5YKYko1DBik16 zwZt2(wGXG84NJoSn7g*A25R)b%Vic9Eej{XN^kNanNFAi^GLuKf1-Z({=8^Zs=e`4 zcD z7Y&}l!y+6yeBOean=x{3oZeHQdw0=@xyN*RS6=;WQfn~fW$$fqST|~3L$cxT?)k@%9TC3iSiYO zSa#!${_e!V?JWQke3=%V(fFb5%LBV`P%fC8YEk{cmk4ayxz+~BecDe+(r~U(zf=_7 zD4^Ph?}v8-UJjd)n_nBKBqU_^d_xLr&pO`|N|&b7cel6^Xw5u@uoWv6Lo{!QQB_wz zTxk5U=9y?QzWS4wO!yV^8$^Cg?*&sMb)$T&T;0VC`@(?%;(ql2;lQ??Auz!e5WJU@ zQ}bWi>B)MkO7Tk2!5HCP5eexTU91)Ttuy?%9dxxCM#X-g=CA`-PqddCOW8mH;U`%K zw4t3hDkqq47$WN@uz-xvPnH919fiyPi?Z&gWQY4rnL(7?kN-_0Xf5L$c0xwM@!itu zr)3F|d}b3WA)&0hJEuj5_SoVKngCVyg&G6f+A9ZX9GLQNp`h9+X2O!3?Bk0iGP>V7 zFz(nqtwtAp!wKA1!K9DnnkXakA*xg8G7XY-&F?Td)^2!0zIn1}{?XR9Gc7r!c{#gA zkhgYF9&(!vc_oC!-_Pm48+`c?YXuA*EH*Da+cu|>oj$PHtV`M1!S)0}V_3397ZdgO zpVxPgwvn2oJCSBmzsEAy%bL>m~1IB(vvx6~7V^+Ar^#66j&MJ2tvD!hq3@Z}nxevkJ!B>(Q9BFS! z6T|5H0`EOd*9U8M0IN(OK$i6>W(};qs|$b`0OLE~pg0O<=0xdKZ~e>I?^-;DqQ?k+ z;cz7a0_D=bOhc%rxBDZuw~a>Rm2eO8vl48F+%DrWtW6%TkOk<#&xt3Ie4YOit_adW zu7P#ny*-Uudh6UH%#hu0#YO29$ZqHWugH1cR;y(0j8=^~>ck;z#v5`I;ZEEa&*DRc&JF8@mA zLEvLa>3n;{xwH1ph)DGYXnnddIMWd5nV1%Kkytsfmaex$tgk!{@2I^z9SGN1Onnc& zD8@<8fUjkVn&g$7C&7pHsPAM&Kq%XW#^{aN%-|^Aq#*d5y)0ZUT;NeHn^JqjG)*sH z@6ywm=mj45Q;s@I*dQu+w>T)!u5VVoiJL%9YJi9GW}1=^`atLJc>mb*A={3%b(20T z74Nf@xdhrpp*8@rO6yGt!7o=ZdCuqej(UNF%9<|W1le;)onfA&cpdh~65*9Kb$={W zfp$ZQf#(N-N~ytLGzr$1_a_X!K9OO*(N2%b{3`W&Vml@PKG}VV9=$zG*K?9bPdrC6 zF>|rn3mV*g;F*WgQT1`)^gXHN-(_;M+a1-(qV{l&5!C?zy_8~k17~G-hrN1Oz8`3{ zT`wSb#QLkjm1Q-s=Woj^^u&)tT_!38Ji^Jiifri(>4v*d7X%Oh2QH#uIWSWVv@Zta zSt&|N;E3bb^iV~75P9lp-cN_FEa?t(Ns3;Y+JKh0>fyHH-}*`BlR$vnCgpumRqXOB z=iZn{bYVsZl!&~>*aL9TX&I_F!D7wFayO6oI$anrqB->tR%h5%V+o-wgY^?(Qu()3 z2Vc+u|F;et37t@Hs%3y(E50u{Ngv%?eGqs?2UEFRd^U@J=gT#hTAXl`!J%xIj1pOO z^2m|X)F~90qp^DzwFGk3Mb`}UZ%Zo8L&0r2h1REZ|FKAQktKqD7M6=6#!_XGBaPctNhN$?Q@r zV&Rb6_$}+7dr=?9?MHnSVsDr6zCIJVsg5G)ae$gQ@d%Z4*BU&kRK4y`wTVqjlLCS? zl37oGv^ZI5Ws0&;k}HdZiW*qP(r2^Dj;ggiAg=qE5M=#!BZ7o{^sMvbNz|fGGW8&J zZSdXdI(5!Hb}w~TuEPu;08SA_Gnz!xJ`91AbKS04k6reeKHwt%O`2A* zJSox$Ky&~Riunz;Cl=X3$u7}E$oiV$=Gbu ziE_a4O#5(EUqJRNy*lw z_|7TDp&p13^c4a%v`=(2tusW980Pfytx*i}_OjkcZZq0K5?-Y2qat-jm6cg`yc|)z zJuP%rKU7MR--4=Xe8AhEZ~B*e#)7?tH~=~h7%WKnTL7t|6^^)K8`bCCS7ctj^9~`Y z76aZy9e2o)#L90pG@xH*sxpRqqZaHgT5Nk0*gEYR9LYTpb*CJ~gV zSq;4JT}EGxj)I8lMofOO2!^BNU`fKuanm}+i2%Xk0QSIa-I(g*^+2rIVA*%*I9;kL z3x$L;2;4r0$LX9eD|evP4kN#7MC{OgVhZ*nur%cO6pDt(X$U=^B#8ie*8RHnMv1B5}b%n}` zD5S_|DO8yS`oz!We1_?Arv|^mW7b$MiJ!F{%OF7c&E^=BDDtNa{CA(UcP=g;+_H%Z zl;rI6+(lo_GSjiF?#-!!y)Yn#00^c_ou2+OU#1zfW(+MNApb%!4kC|QDpI0#Y1Tp* z9tA9C8~VIAELoY}RvoC`D`|MlM1QW-yVSd>V)@-}Y1?)Q(>CdCN$>;$nh0R7Ca>!D ztH!T&*9-*$023l^!zDh3Gv@{YOBk~Q3mO6eJpna69z*qotcy_4|Lt^2z^y}AU0r>1 zxRwYoVwu=9s4G4+7H;iGIH@WSHZ zPg|Lr{l)?crhuup5C6i2JGX1QPTP7*01N;}3>*v1)TwYp>VYV;rZPZp#Y7|q{^kRe zy5W-uDaT)|j@ZJBGzsF++kA;0xX6t14g691Pssm+e!z^0&Nyii_;~gQ8Zf(aTQj%s z_JwAlQOYF28N{1^c~g@VE;B_MvkOr%{1TjTYl5Epvkm}J+wf_rV2`#fhJbH5xQ{pk z^idq(Z=jj+D+h#RWRM^s2Uai|SEK<^qP+Z@(tc>pzgyP}SDpj{ED)VQDllazDhg&> z2d=gSvdElKp>Rj&ROZNa*u09u1=5`*YpXD%Zt#xwa!NTiZl8s;Y8C}i{I^d_nVNX0 zG^mSglMI~EL;kb#_wJz`5gS*81O-3C!oq&S<8+KZ_j!pF3PJ|vi3~W>Scp(^=s>SO zve_f{GJ;6DN`MLebUR>dp+Q}CaA5joy8-ZMa{WeW6Q+fpZ_8XghPK=ng5r|&?J-7X z{(aNye1IDD8~+IPk&7o*`=1#Bh%|hf;Y3y?ABXDtGk8nn!f`gh$z=R%?Cs5)&go1LDiw(Mer5!KDukF^M(C8lc7P)qrI-OVmPJ83 zRY2euoW!jeP6i{0=-CY22Ll*Lof2^ncXY9`FFt{Pe96(-egzgqB^n&*LdtW)ouCa1 zA!TJPT9;DDHH6PI4->Vbj5D8DF?=&&*0j$gzcMEPgF=|1HiK$3uV*H5(Ksww6n~rt zdnAb4%dvDeU0Q|qbRvAAWLMRX`c5;Icc4a%VPIeYkS@E7YXOTD*3^i_#YIj&j4;T; z5$py75lBS9NQ25b00l?*(3(LI7tO8@A1ikH!xDNnROQPm19A$QQd7I0EgmqB0A6!U zbm7(bGFrWX2)m>78)i{O!6IE!k*1**Q#zlAP?txqV}>=q;{b>D%T z!s2+R25=|~ruUAzq29}ul51X)o=IEu5)lUr_*#B6TQtIRpHiWb3I2)Sp(-dU0xI)z zqfb7b=x2_hCIK7dG!K4|kdST9XRV&~<{+T*B@OuG$SZ+HqV3cI!+UBi&?04Zy6VpqCs`O| zg0qI1QU22^7g4eUI9`F-&i=kC)EYNxSf3!^&TriaPxI{wNqO1>LL5|0f^rV$v&1iq z01(#z7wrC#%C_#u{mhB*Qsc==UEQtiZ3nOIUN{-TCQ84{x>g_k<+xcTRNVaKf?GM;fcMPP)+hF~4^ zUxh+PGlvq7-S(HPs;5Hp=ppr2mU3{6u>A2)siL{4fKl(%RQ*N9`Q*WD{@4(L{lSGk zsceSS1Od^e>JPt=Gug@D6;k%vw8KYI7| zY>!=uNvI-*5MeF%QowrIzksD5nBru_TSP|j|A5rU@iVz$_}xYKgy=2 zEBwC~wQWpNQtuess?sTbU&kw8ui%0V<(I8fEjUwWt|*AZlCj`aQyUqbm>M>)jpX|L zM4vnc%u`}x=d;(TNypHsG zv7l>mb1;+7%7$7B@Ns=#r#0ouP=Ue?M9VGS6G*N4o&S3|FN|+{BGQoW!1@lu&@=+` z&npVp;=sc0Gbq37T>2?E!yHM#^8-l78@n6O`ev}rXoeIozwA?NT&IS$vhO)2Y1KfJ( z`RCjMONr+aa7RQv2ejZh|1p#oECm+2IIxS;4Nv%oIPGCXe&5_6go~m<`DsXpPdc@0 z)siFM+vcc%kt@$XhEX20>`g4|Gf&k^syd&qNjuN$E6iw+G8xeD;?N|fI1a@&0*U3T zOeQkr$7|LuZWkyf<;N48*HRUoZ|)y)!^PFoy(D0)sUK6`*;d}IP%eN90oZ!M(1hY( z3jy0H6KiW)Az7{nz;}_+e0I>>=)C1x zX5HhQ@S{W+4uqQ}7^btnmQA0Lqkj1?P$E+ zKS0}vRyr11LRnN2+NJj?e^paZ{^d6(m5CcUSqa&k@h|>4Y$BWzxRi?APcbnuO{?P= zwPJq_L=11E6R1>UGh(_vNM?qQ5o~Lt=RQ7km2EGK&aJ@G8UrY&y`#s*_ABL$b}Lo` z;GmL{;d5^nwW4wSeb($IVl8NI8g(`*6)Zy_;w%PN_Xzq`Z ziHXCuG~4yrvXyf#XNx}B`0~LAY=ed-9sA804ZRK2Ea>OTdDL;BRh*TwNsK| zzIz-?>-1Faepa?sx6mI==v`QWMZ5*9X-gy+K4O56&ja14F;hC1Q-S0{g|P7AD+5D` zyXt|v8=eegIb%nGj`lA(N(e-JX`ALtlQAM-L!&rpz) z|JwkbIXE)+U%=q_?=p)8Fs(UAnRuUKnPhPsgKh~_d0Xfl-^XOCyJBoPdoMRGh4t33 z%Bu&);O=)P>Q+npnLgw2blke<&VJCy#{aT@zdUce8z0RUCyWe4;0^%e20H7eH}_&I zFrpd$l!fXuhZRn9Nj%`5SQXi>g+K{l4PA@XWOm4%6V+rk-L(!vVP{nLwhG0Bo zE2%*NTXuc|NBWuyYLkp+o`&07D?dAd&(bBFZxT?kJp(9rC#OAPv5Y4Slnf!NOMUII#To?}6U_7i*$+VaRywe{8w{Qc1a7Z&U zK19s(`jDChq~Tm&^uTl23-(S*V9FZwg&ygk^R&2_0BjY7na`(_p=$CG{9Ln635}7- z;W(%?P#}oSyzG9`FCOoy0Mo{F>h;Y_*uIok72x<^wB&vM=rM?e0)SqkS&;5NmK=B+ z@4fxKw*3cFt9H+pw5>N^I^N$rGTyFde&TZ|l&7ieaL2k|!*C3mia z1QI`8+sV=p3{b|G*XP^q$feqYg-SxYw_BT!H<+7Np?nf{RZI->6(5PZV4J9{umX;(IfirKK})Bo@d+INS;ywa zS+lkn)RYDOX-To?y+&aK8h>kr(4sb&*BBWJT+qJ4`JyoUik!65*_{lf+SzE#oV+SS zfRJ`Lmd-f6NK}BDg7jfo<)!08FT(ROfF7D-0QBq0Qu;2|DFv$TJO`8yl#ISbp?n@e zNTDT4P~&Cdx}5smu8xlD-X;`{YtB?agHXduUJ_bi_I6YFtN&8!D-Ed%b6aVF5!4}& zSb@uCT}G<1>bQ+4)ptkI=Bmx{LeyNTBc4ywAvU79Yq8Tp%y$UJ)oX zXo1?r<{l1!jvubeA7J^!x7{CU)DswCxn7eD_WJCXlXZ;q!|e{)aV_(+vZ~)mZ4U1T zMs&410z|WK3or&DV0W*%Y>XvUZv)p-;ZVsRYK_+T=N+we%MF?h*7>THM_n9Yp`pRy z;qhc*zk*FfdVEFr@oh!=7_Zn2*nzRBY}LndcGZLHIQbKHiGJZw!?UQ7*r1H!f6=#n z2{c+gd%CRCd=4m=upvd}uAQF&BbAr7+?N(Jpo0w9iJz_dVp<1BEJzonD^III{-aqO zEMemnam}u}JB$40d?gWo#d)$*cEkQsh%WOs`*+S$K-ASvkp{n2o{U#ESvW`p$Mg^E zZ0lCrH7Wgl$1}U@g_ZvQQ;sfj%{E>6?)Q~oh0Kz85R*^yIF@EzEdRkWx{3JDJX_MId7;o;#2 z=Ha~Pf)j8_?+pb}xxV`$+qP(sh=lo|+91|Z2Aivc8VBxqF8uXZu0y&|G0 z`pjDn?2GkYTmTt54{$Y;9_OH=i)zH6Gk*UK?szyI(EfbK@ZJTT&j?e_RW;`n8WtMN zr=K=YATDvrZ)YdZGU02R)m}4g|sKy`)TXeD3*yD&>Ft@s_+Ho27M;Zdz!&2#-^<7s${2AAIOKPf3 zN6603QSppQ*+aNVrdx}ACzsWe@>M*lMz%|d9`CMHwI30TKveU)ElEXF+_cMQOad7M zN@|!cK>-1to`M~9mOdGEEhWFXoF$enrsWSB$a<9opIrGV|vO1xU= zJ?k{SiItgK(&&d|>hAUeH(VG99AYmcBZWaqaQG?xW7idcxp{3Sm8s*>A_hFbbeFY&mD9b*6`RTi>r)LM z+e*~0IEj*Pr{BX;y-qesTn|D?Y<_9L7!K%QZN+}v&;23p!mR(cZj)*8y@-WR!C95c z#jSBQsy=sWM(NPB_#}=5Oj&*w5t!<-WQm(3{=MemGSh6hkF(wq(YI+^N01 z>mc+Bru@C%+~|0|97uw0aFt>S>gh6!;v3hrX%_*m_y@DaXn>-Dh>8k6Zm4%RQI|GB z8l^irHr9amX~(dl$r4V~Q2Kn(YNfH-Yk-wD%~k&2sS0q==H$kEh${7_6$Qr?7yU4r z$JCyAyc@)^*7d@?cTV*K_|mEq1iJ*-RYoH!&S%SgCo9%q(qgK&_Cvmu0-Tz9FCewc z|BI(@49KjF)^4WBuF0BevTbv+ZQHiZ$<}0RvR#vH+jf1s_nhzi@5g?2`(F22>r$72 z%I(oih)l9i&n#t8js2eZ=gJV=*jQP?oag1#0gXme3{V0Aiyr-d#tsc$pRuu;9%Ia3 zG^6s%T@Al{Ov#5S!#T2FC|7b&hx4_f{a@Fqm7X#t%r^eHe1@Y#-~yCjkOD(FWYBV~ z+`%sA{g3ttVf^aTf58p@r^{USe+QMRxc5oTH!2bbmUZ7FzJSebY>9{}p(4*9FZtL4 z`jGe(j1-mF$`fEx2!O0>mJ<-3d!d}x*Bczq%wRB;jmRn*Hq3LmSWl58`D?kj;GX(mtM)sATEPPl`BnLU*720Oe^HQ&%S)Ux$4aJ{z<_RuGQ)?)voc-RjDofFZ?;3jn{{D(%Mz}bgcb*lD(k3es^{s?ds=5RTO?E-$u;Y<<2!|OR7 ziI%5b#mYoBZ%k+?$aK=NbjAo@WrhcLfYjMrO`dLSkwsmxZ5WG5G_nD}IF~PuqZeAc z#R!5iQS6{L3oYdj3gT$N+*XsLq^6dhEuJV<$E@iv-%zG6{$u>d{_&E4Nve!Kv5DvU z=Eg2DaW3UK7Zkn-xZPn|q5<+ok9oy>B1>Xabc<_PYYt?Z^itDNm zrAd=UKQF%-5bgl+3eZ|Dj&UPs)~-JLHjO;PtkR6e`Qh{oQST<&14{AI1l=H}gNwEL z!w?Hn7FN@~`%+8i!G zJ(td;vpS5(ko@C&|MG?hFxrOs?-E;-ukjvN+mI* z_y2z`^p_t-_C?mZNvdCj=|0 ztmAp{uzosrJ|A&~a?<(Q9|SE^>{ED|FL28Ztz4E!StOmSFc`h}GWj-PB1I`Jea~p?&qRK0N&waOLb5?`eyV2OUZlkANy{os+N2OA-v&kC z7#TVqt2Sq!o?616uE^A{DOZqM)~OB+59fNo7)b)z;hr`2#CBIUDQex|M3VJKaIND%zHVISLC+b5(^k}L=VxjJ8c)w3N7RiPYAUT;QSIk8d>C43%89}ph3g&Ca^+U! zhZ)lHarUTgMD6MB)5H)ceZ%EfosD|z9MLgRg@65` zFUqo~;C+8ZAqsgC`~kGwi?vpiC=;E&pb+;+fWPTuitNnm@4(7UpG++pIYlJ8*AfZl ztj7&T*l3Ii-_WV@5j827Ufuhakec#ag?I0%8^_~WsDM1Y4Q-W4$1z&fLw81f@7dc* z*>oa4ro?DmP6o?GI+19csZ(FEhDKwk42DA1bvXQ+gBN_GC2bi_G|JsG842A}cF2B9 zRA3d3n1-8k^pGe1FB44=%AiJnq#iRdo9~GCa@IUMX+WQT=f><^Z7SS9ub3>{pPyM* z77imygu~@*s3z}gG!DCffp&LdLV_;W7FVc5ml=nMT~c(G2140;m=C5 zzAcN+s|ou0wNi(bMAObQ7!_|GGgC3&!)xEof_C>_vW&shu|Ct9=#@$g!`by{v*$j{ z>#lV#gC1KOw)?W}r*7mH{^)e0Gv@box!{SXxv|$%RPE53You<5@!>jsq@Fj5o?>n{5`)4c1IkKl(*q?kLR20ZvfNFS}3x zScg=b4K>X(<{7HtT_YSY{ZdbdMu`(G@2O1-2t+rnG(50E=gf(!4l&3m#D#5?zRcz5 z(_zqIMy0@lw>XdME>yOgrir+_y920Y(<&LMt8!D@x;i%_@DEvR^*}(H3r^g0YEJjE zrb&)Sr2GhgAPUO_zh1VmK{Ro8IH9hqR;SXmKPu?^c5Fr!NX9XKn*C%fNWV7VB(U^B z;6d%xR4u{nu;TUdbh)MG86BTzKunfNvzaxcPuUN>@vihrYC*>+Em5emEw=9IPK-@^ z)~!ofj4Qp9_iOD4b0d^K;E2h%vnKQk$@@}!T4PDmce`dDZazK8@@Hn=Q{pAIPO^McQ_Q%+N^b)8bzGDb386a_nkb^R z{O^C)4|-L+Q8VC4jmrwwp3z-V!&IjJDh^%$pW`J=_K44x< zX;dG0*6j7V#jDnFO2ng6!?kJUaXtJ0(R(oBvB$=Tq92U_&CveFSm|od33Ytb%T{X4y&X5cf zBT$cyaEs53(uei$Cz>k>g;JzQYU}DOT#bOaQJ04xGY1|6c!LvVW?0!(n!^Yy4u{So zsjoMgtV5@|yGiNRP+Z38h|3r)wnu_7bmYVl6L{$pwVLSQV6aD8hpK;nQ$u*do~mnq zIO2~pkY=gl?S(TZ&PZRxJTi1ASZmpQ({L5HeLC&)n~ei(h4pD|O2)wdJfEO4Nb$U0uS zc@BEyr}>POsi<5XVO27qTPI^Tjs^^#5l`#y4Rr_jO1ry@M0tq_i2%bToiZkfkZJ|a zVk5q=9ANv-FZbQv-X175tWzu+4QF0yB8DjbYf9BlCoIa9?^y{1=vTup5l3hzJM-x_ zD7krByv3Y!?@1PqcMnyU`!U?jMngxMOzxNyuV+#mV`)8?j-289Wj9E>)XQTkI0T`8Lg)MG%wUe#oC&aXDZCgM9Ij;e zUe`zmk(|kY`o7uQ*Rm@s3dLA+r?pK016+;Gg*F2t#8U&9@RiI;@nv~2BCg}Bf3^MU zuw)4ZA(^0OHxwMj1FcNJLMWHa75}t-oos@X3gRgaPJM zlm!>cNp5R;5rux}0CGzpmctO#3? zoKEI>bi2Asq@(PGLUj*2$rxVZ2;LrR|K_21Xam2Cuadpn95=4EK4x2vz4A)8-+wXo zdSt<;O6!jx#wzWWbR3`?{|geHSUg1JX}jU%S$s&Aiz^x(PmkCe@baC22WLj_O+PAD={99M7aBEhBkfM1#Qn=QLR_Tno$;|)!*@;y&zWI48U;oOZ zN5()E`~U9?pom(Pw=KdSWM74d51$MZvrjH=+I?7cygmO3Y`aR zvO|jxRNK;mr}PO32y)Hth7JVfx>TzJ)EH?^{-v6*EcI(Z+DY~{y*22~4X8pHTJwmK^(Ta|5rmA2O1^wemPoQ~Q#;h|todwGI&Hq$qhjCK%kgSS2?VD4qw3^597QqH1owG+QMO}a zt=(fsW_X>uS+2cOrn6XgP5EcphUe)vicaR6&_3UuY~)>eMJhE!R38bQz6KV&gxEa@ z8f=Y{KF*M~9{L!?aO@~crh6+bD!VsM<2mHiQ-$}Fh#-C=D6iJB#T~C&uVQxY|6h@W zUJaq7**H_|-yej8UxgR!*w=E*T;*itWKqH5OSf&4w)+9watZ)merH36bFB5aKkOce z!VarZ$6gzr_meM*e@{)7I#2O=0HnHXuCYKG(nu4%fjYHs5pdbk8B0uDR`0{ z3bke)T6ptK9P<6H3oVa%kGfld3}=qQ{e=nZDLe*L_?2w<_3(%1SDPoun^opGYaJ*K*)@IS z(6=WMZfDNX$J}Bi`EZ{!l4?}C3_|75PyUhZ*8%u7x@>!r^K50HR1zwuoaqO~i+OtD zG&ctohkp@t^JT4?h#I!c5gddEnAy3dQ7$IHIGh7)nk?5^62&44(n5r-T$!VQpPtq3 zfilta*pC1`w7qKGJ&hU8I zU#hl=2&bi!qqP8|e;Bbp#o~YuRcdM~;IbQETgzaH1_OTQI*)|wLnl?V5;dit>tk#j z57-HvaXQPMGVVB%9SXBkrya{6rbXrDlv1P>Hnb}6VjgIwWd=~6{19&GDT$#m>}`KQ zwop1`+Ji2yp}$RK0oxh;=;GS^?yhOa{&Mi)S6kI7@N(8r%ui!~jECzG3H7ErvoHO3 zd5ODzvwH`k3~Z6n&shNbL+U3w$~XL8dsK|-+Ibzm}$RFK3bbEYX?GFg9?$4 zTEBJUwte%fy@VPjO**ayXB7I5q9o*4{zh?K*TH{-ls4%37b5z?hb|9OlZHJ2?)l9` zFY}hE7J+LUMj(x>To$$7?x9qO9u*y(N4#AuEFxk*lEV+ID&phg1%Lg5(oW_@tmw8R zWS|JI%;boRjh#Ft$M$l6*q>CGa40vVept7QcU6MyE>Oxr#KaWVUYFAcaJaeV149$Q z_uPRCRRlZsrPipSI8yrI3us-EMQI1WRUMeh4;?3>p)c@o{Sq|AeZ}ZiXZpbWWZ|Zf4V0*qhk`)&AzBSXfrdObv2E{cmpW=|R8FJ;zj!w%HHw&>JyqttL9wz7 zF##AOTbwV30RTh4Q8e~<+Pq;^L9AP)1s1!S=%d~nhcqTbxm0^5)7Xbb>2S|GZ71P8 zq#F;IMw)5TCK~INihp{_)Sv^)~RdE6Bvv^;Fl54F%-&UOQJNl*bUDc zH~@yIaCnoT{Rm5CEzpxSpgR=2+T#wBKNTT&HlTR^DwE89SUAwUg6S@4Xg-MkI0LHV z*Uqb8eIo4F9+(NR?tp6bt0hvwG9$5Yd|S6!>v}O z`b1q$I<#=_56Vj^w0 zQoT_t3nJ{=AJFXhYGjH`s_=64G_?jbw)?+Z30xh20b6C7rY(e2suZ%`suw-bTgfe2iSryCl-5Gr19qL2BT~iGn*ln z*iAijd{BkP-^n5s8Tw%0*Mtz%fEIQXZ7sEFM8_5uLoRMXwzP4(wm;Jxe>>=v8Pk}mf^Qe{2adH$Pbf?w%+{G zY2Nqd8PfLnz;6P4?h5XW1kSTgsEG|OeI0+-`h{18eVnO^DHq8qTbxgWNC=hrAioH} zPzTMXO1ADjhzf|28h!oc$~(a*!{9K{?y}zwn2(G!uLhYvU0*7-5Ebe#g;8z_ z%pvqASEu8$QB$LfC^iO*xk@{#G?BqwmZQMq-S@W-;vT@Nmu(BtwAI!&yS!9b5U(&Nw2p6I{-J1!6|VK4 z*CES(iu9fuzq1XJ0n=AdFr}pbs1j!xK2jolmzPXYAE-mGHc)ihdS#t^up5G{$@Ufy<&`KX) z$0Z^gxvKNpF}7z(jx~N@C=JPcv_&ba^K4><09ZqD>T^O&-xbSHB041_r*LX|_BR1- z9SX20o6N0&!=nWVJcF$Fh_LZ$uy%oF6}Mr7>gpoEJ8ZFK!-2(Q2rNFA`=?V65;NF) zpySEkncFQBRr=!OSQSk@60ALGD-sXRO<4-DY>9>M{fHD6mWjdb{v8A_1_B7#F*N+f zvXWZ>h3|byJi*->aCpcDl#O1}?r|xJC)f2()ysYJ^%nAAbQKCsud^uXaWkz%3EFh` ze~KNxjGBGn#$~9`z>epO6siXBw08aBUku1z{&2BOEI-A>|0FT1mO=Gj97~gI5|5}+ zHCzJtu4kb60UqU)Btd>e&($V2Kg+ze{Nld9Zj`ksmb0MDn^L;#y&gL=^%g+gVWj4tq30y5j$Ph=({Z6 z!Z+T=ReNOK@`-KUQSrw|bOeNU4&q{P^?F zE{~lfytLG`WjtPnyZ=xwKgD~L8Wx8Qq;Bco1n);rc?flRW`Zpp*pVwY|H!6N6EHI)U(j-)CW$W*ROZkrZenA>X8ytQ`S|&40@Xi@M&%KY z6k^!4W~PT%>ydmzhlX<8rvN_6Hp`@Cc!$;(ra8%Ycti$T$!F}ox|=ndM3#po7 z0FO+}KvBx>-~b@R1@UoWCQ5A~Zl zaZokWe04XW!;Nu^qrcqJdp_<#j$+- ziE&tx^#Z$%jraa+nx@TAr96QAV?5@bl9#s)KgaeJsrJ5!SRbr~BpC7A76u~ah?Y8p zmRBQ2el8EQ=JS7nYG&WjQ1xomf62%U9>GGDrT{>sv4Zwe^p#qG3y^{XTwggwMM+>? zQaH>P2~7EmP7!Gzz%#wrjf&t6uV z5{pYn0AYTtZ_i3_V)~aG{WjG$2UQ8XMs!E>mNad-5gt++Q-GUA#v%TS-2|xhxyxLRAV6oGe?QA`tn|kAz9}>KoCy5e=55t5W zi(|RIz6JzDW&8JeC*TU!u31Bnq-zQ|kvKj`KJ5n`BWIYH>8jU9ML<3dQH8hN3jjV7 z7%1nYKn4!#Q!dlGgzwKr5cKjZeB$qpr*2Kdf$Su44LA6tzy8UQnyWGe3r}184YvaQ z0?e6oj-Q=^RaK1Q_kYGPXk`s*5^S)&Y^UA;Jjr#xXaM|cU%h!}3Z;ku1guekrqKRcFkB<1?wd`dNWZeN8IAfccN znzERg&TB!mJf!vS^J7%J(ICI2c%M|P`h0j*R0_edZw0C^@%>+GfWJyQj!FJMM9?2V zzWD!C)}J(jnV`e5DIzBCrV%@Sf5RtE3W}?JPfxWHnzasFAkhF>8{RN^iPZX_ehnfT zp0ukf+juM@e5Ir*mDAb+6Eoj=5_6)%<+eP+udqz9lce!VT+X5O`ZT`N+;Sqjqd|j) z5SA0oF}=0tyVvVc+6<@t)X&kY{KJ5|zn?h6W1JlppQqf3Ci;tcO*r>J_!i^beL0(E z&REhAW+n$QNVVAviTlGjoG&oQ(!m1jgTb<@@hZg(*xt>`)^idTmSBs@jus`#)4;2p z=JOVd0JI-}q~O2(q-_HjueKQfrZW2=;WiUD)A&W5lG80yS4gQj$s>0xt&2 z-}ZLtcWQU69uYRL4+|_;7Q_AdI0-Fn^6feI6jQ@5JXohhC-hU}9oCO-b$S_%)(rvA zWj+AlB6moe`x6I>#Nq=5dlOgscd$HZ_oD? z_qLtPlb{@F8okJ7739kGl`cVfWSTB#4XbVoT5T`moPmLXwhNgq+gI)>PZK`M;b1@< z`0Cq&$6~Vs#D~YjTFyau#mJILW4`t55^DoGiIGMeSx6F+I$y>F}ie;P64x>V9E*9-~T_CU9rgPYYG+G?K< zZ@@Kx+vDEw_0i7r{s`Iq_Ry{mw#IbJf0|r4=K$qq57e2L+E7xOVst`3zo3 z9^%KoD`Q`m(*YA3^+SrR5{gz!0lnv=swa<5`=A3G&gn`cLi(e~%fl>vi zm&^Z+6e???QBng*ofVZb&_AVidF=$I67DXgjQkH|x}l^xfl-jsvt z`?co$AMM^ulLBN-d^p;49#uY(stEQf8CMkiLieSoJ3?jQw=i-+BDp`9Py*@56vsDw zK#1!dc${L|QG@J8Gc2s4mT;MCjwTJ$AzApe?lnZ+5@2{xrI2zcmW6t!%2#40oC~@c zb3!O$3La3|k>0hx@hO*Ze62~x(mi_}>_>FE%dH{T4`k7>gITJ#XFs`j^XD{cl<=9ARqohOe#0ozvEUcFB z!q-D}5WxjHSfJz*02#9D$!pewJ2&%gtI@k^ddS?{s>qb`{g7intDjWp0TwSVK6Te= zh;caX@iBxxX9QC8NMv51QH=ILbG)>1&*POOn;W5tmMd7}SlYz3rOio}#>CF((}f(} zvw9^Kap*+l%jQ-3s{2d*-}pvrp3KgFZvleQD=g9J_CzLW9fs== zoN+--{*z744?Ary&HC3u((GqnE1wPs=g}~`DK~r#;}RPZ0UJQTfMBA{?Ph3X8S7`s zcC4>0opP`q#-2csd5#4~T47?RBkCI6B5;3WVq(r|(3%h9TPM?hGGk)>@vY6yC4)%wSu#XI?AYn6En(?YAwj{Kg+=6F+U- zgI-L$`gpi8_AGAQ267kXE7aO9do+Q#J~uZvVD+o*sz;;IXskrJaQfa5o+JA-4aXC75xyC(22&t&)o1@Ng^9Pmp2 zu0$Go1$((71@b3>4pTpyC_RWBw|(%5b)_F}b+iRzdH z%3~raO-p(Odgt(s0*ArZTQ3_%uVW2IpVQ7&_NIinDxTq!jkoUm_Wnn!n#k5SVLUGG z$`J*wOztw}T5e;e84rz7-lg`;)61P^eN~35La4~&X9`{$mwcBoz(T;g=pgY116R(W zIW%k*$Yb`oQ&6_<2=jZRvZn{j<@tK@xY;hbJ)YjH^D&E0Il1vSe)Rnw9^WWy49@Ex zHKmRPnwRPQO1lR~4DY)tk@4zSI=#wWIoqQOD{Os$HmbQ>j^r8{IzB)Z@*PxY#5%EM z`xF%(Jmel>wN$DUGAMoDQ@tt&0iuUgi}mOaqv_~{P}mQV&vaS^KB;vfBDB<-@0bYT z1m?^A`hro?$b$7@5lCGPKGgVLHSo-Kh@kQ5DAIMIQX5tJHV3>{4};nJB_Y-aJ>B%o z;Qe?Etd@hbWwhMor~&(dKas^5)jCH*lrJGp9)w~u4E0WYbaqdMBASjO`#2!x3wOP5 zqlbtMX1=IF6GFl7-wOxB&Q?18Z|OH|;>N`$x=$fc3i{_#n)g9@k_R z6jl1L8H%w$|BP6F+&CD*TTnMVpT#&5YR_~C!*L+b?9-{zgo#e2^lew*h!Jk#I3Te} zz)!JzOKme=6w7p^ok8x{{aC-FJM!x;_wnRlTtGu!w_%vE?u_lwL)Avxcm2JsH@2?n z$N|Q#5%$9|ho|@wL|6z3TdN>((I70sHxmMyUd&YPX<1!(#$<{bu3Am2p6>F6seD|_ z5)~oADI1kej|E6}!*P}9O6C<>9`9$n?!_d z8FXHcmEL99w%pV;!ny7}g4OK~e@5?KV%1rIGGp>`>jrc2^j^?FmN?Q+g{Su=xtV+> zBXC(lmF0xuFI;;Vd3x4%?y23s45zP{6K??_Iv2esGtVkqXAK4}z6%Gaq(=N~{D`%BEuOuzbXX+A)tP!f^kxm+AWm}FH5xr8&~r_8-xuiHsxqAg2+2h_Ji6zpjbo-`UQ93 zg5{yoXhckLpOYx645k;am+%n#Lbx1IAzVR)q=YFKX@iR;%41zoMRAFL`qkgwLeqxq z9CDI{ZNsr%tsitc+(6keLsODiNSh8?t6aW#Hhcr`#}Y3_ZL`?e<>mcDch~=J z$on^@!$f;17BB9W-Qow#`g>HnyH86Ri07lHZWV`#KVDR&Oua<+EM0 zm*L)b6eE@yPI3y<`i({EjV})G7Jp4t7G-W(R)qiqY z<%kpEe?@a(lP9|5`(OTY)$-_u6OusRUn~b?SF)usTZ9wQcMIuBG9>KoreRvJNW>#u5VMrrHA| zcDTj;6jYtbW@)FP7j|cXj306?}RBfmx6&CMB=8*WJDgCM+PWP zqE@{JGl4lUojzX_-#HuKTQzEBM{jdT?vHR_t+5V|bt6LZ=wRcC3z$;5YQ1eYJ-Km{ zf&Ao{vSixsggLzVynB6y7BFNZ8GLqT=Qc|kzSlWD4#E9l{P6H@R4krXS~)qgk#ra#&BxMV9Bdd51sQ{M$~K$JKD9`sF2)V-nds@^#bc|8oK24w&N@++nqJ-uzKhSs8M&?yQ|E8=PC70`u%$Zw{cxd;(G}eUtPyUDGj|@1CQN}@z+)t6z}U{ zQI&GG*YZZYe)qfM{yqhny)e?O-K`LlISuQN_cto7mW1XH&y@O5y4r9F$~KC&rq#VF zm891J9a_AgHFF-qf#K0n3syDWV@jO`9(8pe#G1URS;w$@8n5KoVb)oWv;_?lftFuw&y}!P#2RV59hurViLWa!j$j9Zf&AGARHDv7FxF(OB%--?Mv>{aJk9q?5a8xCo zTmk|>p%3yuF*(&M<&Rz+=0<6h%6}ZsB}GKQjq94_BiYEra9%r0s?zd;mn=U=ZpU9z za4qetyzfJPtm{85UtQaLUb{(c-jY!%l@amq)>|64g1?3KYKMzxQ>Kp%v_Gu(cD}#a zeDzzdK?Z^m-Yu%=k7`H5TsRixj`NykBSw{I|9x^sPK$QIKD zNEvgEn0zLF07tRk2E^72SP;w`7H9zJK@Z(IlLx;1WVmn>B) zl!28tZ@GHV#zm1vV|~sFWo)v{cm}&F3W^*G@{o3IAa-SE*5abE;i;uR6tzfv%-roF zDoAe#T)xFZC6#)8D4^`kTW0CpkVysdqwP(c6Pik)zC*NAIIeZIpNUpxQ_Fa&ho3|C zV#oQ`{RC7slAgTY(JXg7|KzUpVS(j0x1SVm1k!O16$8>78euS7q22U@4C{2G+6%`a z%aMB@t0>}fv1;xdu8+WiqvEl&$mHr~BCueX$`|>a*boK8bnjfb`rm7O7w$fCoCGBL zEiN)Pi3P`71|mCSBij+e5#$jOd%KGmK(2$v8;4pNB_EThLd#2KYt!L&#M4anyO65t zVy;GWZ};a8C<;Yn<|dH95t}Yy+$&@UUX>CIMy6Z)t5JW3`*F#1i$9;404uQ7AAV!R z3k;tvNWA0S9DLam5TI?*g8d08@PbY`??Wq6TvsKDPK5JCl0@M~9uj~i0Kn69z-w@a zyjQ=5#e$)n44T{Bu}C!L{A^r%|Il=nMr-kDN>sA#6}k-ut^V@sWHBU4;Q%B;cR1;C zk&Ml!!9Qiyzp{q2Sp}IbznAl!af_<%#l}?hi?)mj9q+)^(@fJe>F)iD=KWGiiiIag z1`tm9*#qRWCwqe6M#;}dYaQ%#7T)^+<}Dg<_C=Ke37w^7vhXkc$qKMAxnR+fTB-{Hr708s2;~!*yyb0TM|B=aFgo)TlPcsVkUrpiEkhg5ECoBPBy1a9?`1bD{e(}PBhuQzsC{hqUVyW2I9|<05375| z9rzgf2>!D1{Ji;>4bLvrManjpV2v~&6`buif&lxJxSndLqR+4mfImV` zO)2&*4`oUqpRP5c4}-evty>d^R4Y{9;q&d9CO>+=N=C{;sGENGgA9|HJ=PDJ6Zb3< zI)#V8{!oo6Q%w2^ zq5%Mfr$!eReC=zV5-2Y<&Nt<+FZU=f$wW8R^YU+2Z|kVt(DhKZQ;(FSAiC`pj*02GAlQw4S4!2M3mAf81cAHg8U`X9VN>%<)M5CdT0^Q+V( zT{|V>AiWWieMkJ$u;NUiT=lIPBNv`4lX&bQt_Se<#739#6d?Rv4!EuWdQc`wDL87~ z1c-%M#RfqbRFG_|m&mQDBF5@VoWE!5B4#n%uZqto1|Ctl2PWD2(P2FL8~xMOKc+Y` z9Jj+(yFEXV`Q3hgmrbO|LuH6nCp15PSsx{=88MA$!L>wasLO{N91p8#nG@HWsXUP_m9u_?J%)8Y?`8RlVVs8Wn23N17<1yZ;Gv?z4V zA+S`yw>y|X@R!dG-0$Asi;9<6gL{^`t4%dj!&&?X)ec`EvDq1h4hs0=mZxj;S!IL{ zm+DB>R{rW?h!h~!=xzxBMyisU1qHC!^nRq-p8i@(OSi{!&oV4%)aAHix2lMAt7ZYQ zO2gEmv?KYaJ0FP82P>b@T0LdkM!UvI9ly$kRAne?f)g$os?cW#z(hOkLAs+I<~8fQ*dn zKXFjzd2!|H_VI?`3hapigE`|Yw|z_i<_9j|c=gqA;H!iZa_Uk4NiM0uRs8md`uSP1 zSJwS^(EM_ruPR?eS_J1K<)tg9nFY$)F27k3d7_aE8@* zf5797!R$q6^%VDO0P*dp@k(o-?nX8Wl1If7&zkmi5sv4k^d~SlofswnT(+-8eQu{h zfJ8UIEEO#xk#T}8UrI5jt*y-&FxqRD8|me^`zhBK4sI4HQNQ)Dt$oXe)W|~&j`A&6 zYg6Nm-TjvzMjkx8^85Ya3ysPrL1Q=w{dPJhKE%wCN3;?iKNkxDbPVfbLMQ)xS@Ym; zFBt0iA@FC!P)G8=qJQvICFk{gt*vvB+jq`A`48iF2WkzA8$RI4r0UAHrMJZw4b|fu!>U2cR&<5&z-5gu$9(3B%&tFLNhqNR~4bsW((bS?&}uy!RNuUsF)CL#A3<<@hT;Ki zrT?hrf-g2$KzKbP{HV}yp%485DH>TA3J1UyYcVJN^VF%k2YZOv$-adpXCBdY*0{WH zuP8ItsUk=7)CrZEzqYkp8kV0Nl}G_N8q=s3$Eu%l`)B0XPnpg z-;ZVoXrwqfAt4c$@-kAz_{rI0dlsumL6>b-?qV zCyUSU>-@H6Gbk7owF!FV!iR8QR9*jPCB$sw^%VbclN<~|8wE?TN^pjPB;)Fi-;+N1 zF~0!O*nh>RQCL}kf825Qz4PXK$G>bk#jpxu$eyj5!rPzQZRvKf7%P`t^K@x<)vHIa z{BYkmaeS3@%PN6 z1o2qq4@fuz=$LM?bN;PYA4IRa@?K~(Cc*O96b}BjiR9uGC&s^Glp?9nSKbS!IM@kJ_=FweVRC)Vh?=+iU-p*4qfDI~^;3`4XXg%k1_R_nF_hT@d zhJBV?`RiS#ypK1Y!b?+npn`~k0XT$FcIQ-atrQ%^s-C4GLct#egzY~q^-!Ma-aSdn z`Bq-!4){1TCi-jFbd|&>bl*t}X+Y`RKKRDSE#B=E3OBhdxRy^oEXKt^iLKf0XACCy z|MA}q35g>nv}?#doU=)emUx_}XJ=28DwSa{8p8t4k51>L-_W^pc4%*RB5U-b8*-6hVjwgaQ@5r2A7zHLEnY`_If zyD{d($&H%)$8L&lCx7v)44{`ZE}^q3Id6eRXs>bU=CFqT>o;f+9R{L<_k4eGMeBwA zalTD-8#FR`FltxkIg4pJqHHm=|+(Vbh*RQdXz=b{={(0&AUwvn#*3)5Qic7cBhFn(r6-He>b zb+mtdh*4UuC5Im+Z`{Q2<}z-Dv6=~S<@JP^vtqLLkUA#cXkud(fy>prVSz z_j!#&A_`t0SB0#w^-zNIeDcPjXF0ucp7rF_)h#_@wxe=Wft<6u!y^_kC4N0omgl(O z|HQ0CaVAKs5EpOjFt+J-UAVO)CzYYt@dn`>ATifa4c@+TMc@tGJfWLz6>YX%nPq@R z$2JvS^mT4Zk0=|1U8G#0)Hu=hf4r62^Ap+=M%Q`dwX?!4Sm@+Nw!?XZgUQmtwLp8! zQnZb-DB)MVEbM61f!RZC~^FGjH%L=|J&b@{ukZa`rs2i~s{HEq0u zwBPInO0uKGPp6zQJ-hCUNNOQe9@9nI=jsDD9AN!5x+kEd)bZhs`t$@*nrI>Ae6c=O zuTcm@;EKCH>|n5N{S=A8m075FY;ly@IdAJ^2YjmJ~RL-Lt4JWEojJu6TkQ*mIHFV~^|JAUe*__Zs`UE1u{D*?`-k8pjDU;l$TV6jeveckL>~v|ED8 z1GK!Oy#uD~YaG4a_pY}^m}0Du4vB2J6bY4`HLa`fY&3B3Ce$4jFOzqP#cs)ZN8h2{ zxT|GwLhr}MzKxhik2u}Jl}$=#1{U0K&1#i0nQxKl^)TI%;Iv;~(YmAE^s}n?tXdt# zSl!xT@V=u?lt%46=rAW%cV@&?+#I+*;2XbKR|G4|`8-Wl!eG<4a?d8|8yKW;xiN(2 zDH=nuKXz7HRPa!GUwjf8&(NfKZ!CEpcVU2S4xSdPtP`#e-_8NEMas*nLI#~7G`?<8pZo((FmX=q9y0{9r#B}r{`lJNgmYW&QrVz&J#yv8SKc$y)U_t!n zekC~u=R%&@To8ssawUcb1A>=!JwDMN(tTIXz=v)eg>Y?*F0Om?a-)l_w}PyqQn2*N zk!`x7KCw)sXyb{1j%#i4>=#~74TRY&g@M~{oPCkCmc7YWgBKf9cfx%d`jCzFz@4R* zerD;i;_GQ{qv@RE`h4HNzg1gW%hqzeYuR?oww7(%wr$(C ztz|FQvR(Jp=li?=+8%36+t>O z?B`kJkuo@XQ>bo0vR$kR%kK~Rxxm=!?5>A85`Zz?KI2jYNvf03qtYNO7+_?0hJ10f`&|hg|Md4Ygl9pF;hnb`!fY?xtdVCqOb_@fdYjD4PI{nQnNrc~l zK}C1TTRDflRUGMXOhSbfp!+RJ%RtHXQ=UW*4xYoq=`Pcoa|~U?G$^%_6GFXgsojO4 zY14+RipDu?RyxanMB!{AM>qR$zN<{9`{+Q++q8RP)>7xM_huIbO?^i8PkF!;JUKCa)LVu*widTa=6Z8)3MJ z-fe0mz4{eNjUxRsE^;M~JH)ttJV4d@uJty;>CWTzGtd~q)EFf*A5S;fQJO1H*a6#A ziQw*xi!Q~JceWz=Nh?&Zmv#J-j@*=DX##_Tl9OiJO}B9majCQFQG4K(=&d}>`VKqv zYD^hJ6uf$=aeGtE&fGwx282+&O{d!|)726bYd}CAiY@>*baK!K<*nNwu86Rr&0%qKmzY>pT7m(XjPAZl6#gAWWia9z7m}ym zi8k4MP8867UnG^$e7bNHj;DE# z9Bx3VM62_MIMiXuIvXNRWFW_kIDq{K2RV@N-vDBBP_i{XOZxC;7mB)qHK2ncSdzwM zir6-?Bzg(-+DgA5qpJldNPYLS%FSpz)Pcq)?X`>K(Ih`Cl492nxsmrL*JVB5D(;}_ zXKj*yf?PK|e-w7ALMQkQOQ9p#j_5xe331|E4brmfwH^9^z_)+QeaVBkz0ca}g;we7 z6galw6q|$r8(`|{z$g06s|n_(7WkQbz3}g01LP*KhH6F!-(3e4KZnkEZg_i~@JbX$ z?A0)%Z~#m%Z|#5K(~dT%zY#)^48cGP+^V`V>0ez$`23{zfDA7NUwEx5 z(9D03@qLOfmi3kcIzoprw=|Im)#i83cPz7;3{>NA%4bWY*k)L0fd-N6X z&-|DaIxi2GpNPrGloKT-B>InUYweHZCZ(|@R%|?igTwb%&pXW;RbpaS8V0(04z3$b z6HKfze~Hr!0D1jK!1L_k^-Zh?K<&QWUUaeA^g>W23KUI^jPSt~130n^yBRGFY8q;w zh`JiKdP-C^Jz*uTwN*|B9;M|Cxc;>J16eVOga2C1Fo^|$#A zTB0m5`;7aO&U*DXE_n6Qr0&&sg1^k5bV3PokN0pcFA6of${8c-GOP9o8 zjK$AVc-`UCb9P8_(@UU>HBY!EzOp{{=Ug}4?`+F&{HsE*;P0hBRislQ8Qz(| z3NmUkT#!Z^lP=C@x>?8Xrs-IV)o-$F}>YQeOa^Xs{ z(0=QX9b-cg;$z@tvX7N(t+hLtWTs?<0HCH8XM+$c<~RT$s9!2$c&Q#*au)O@EThe_ z#=#5?u#GLHYSSHnd9z$1yH41l<#ZO1 z&0d4g+o#6Jg}<@gNhafZd%Qk*4B2whbOm@RM+8xtvL?ozbt4+|4Qq#aUBWsyxr{cQ zdO%JE(Xo=Q{W?69ndZbOXp@E22BhtzeO>raGfDSE&G#U!0X0A}`6+Txp%%*_D*q?yO1+v-B%X&#z^5;W&`#ieU62!vXx2sZ{=B*~JpCm_jxE#*6^NVH)ZU0Is4-*rWEV{C9 z*HZ&^>GMq4G1zy8qJ|9X=6P(IR4{lvIoV-K3-xN${6LxYqBYwW$cwHH8NwrIhGAc_ zrj(BqZDgv4N0zlE%T-!+Ne(n@m{!BYLa0E7tF;N{YcWEVQzE}4Kx(R=o21)HmMy8_ zY>o`%_cmnx)^>#B(rXxtE2T~oi1v_9HSh3T`Mx7IaYay2iGgx{Kt*6bosQ%_Y%1rV z?*Wa^zPZt>0*T)B%VT;qx&A_W%`js;7Zzj}d*)z4ngykL=9y8?*Autq_t_OLSof(# z*vWjmIi4KYi%m={yE|!i-{+Bv_r1c5&YGP6u}i5ktC>I#N{@Y)tT%;g?(FS4<;T$z z-Wt=gF<#nxgr3SnpJ&~0b+6J4tJtHER@Td_ch3>`RoW zlec;0eD52+O*u( zl|FjV6|$4qSn>cMp=Lk9Wff72W>r;5fu@Oho)_N11*#`_OtJSvR;f^FV$&(hanA&* zuAtShYfKhLDnRGY$I_b{04vyCyqpj;Bp(|sB`CE(=T*fOOFi>;yD4vd&L+6=$hmL^ zjtFGNf_WF%L-9MlcMv;QZdL#xEu7q+Bi8mU8@Z5`f1j8raOGP23h`H$S(^03&O&{W zuMg9zU3mICDyw}yJK*_LarZdBCb0hT>WNca=)YE_i|Y8AcEmBNbRHW@1m!)`LUuQO zNE(9rNwYQoL&AMIhz;BPa#dn~6suMyK|2sir#EOlJJA%KDG!a6Hq0&oqzZIBWjy@$(qkrJPY5XjTE5y#HQ5 z0)k(l?RE6s?S@<$dH>S6d3&@B=y6ML=wnz>-y{; z7$A1c;k$bm-F__s{@U#sYN!?HwCwg#oc%X&zbY4s_arV807DKBa;K`+ZU?O}BF0(u zc36^#og<=3i>{6hv!fn=c^6rlc<;u0gv|`FnhzF|9m=P0qsyF9%K0)r!J2~^>eYAu z_7b{B=xcqV3lqh6#07TWiYlmSYn9g@np#C&QnKbYwGPzSE9~5Ww-O(ZBE`DptmU>x zqG5ms956>lMs&9OLq2GPfK#5I+gVEx&`y>PRI~u*62G}Q)d0zW`_)}pI5tb>WJVmv z4O2tCU^MF*H;|bI+QF5tOF#XRmL?-DY~N{luDZ8gcUYtYpkALMkC)TZEL(A{Mh>Xn z_i?4VE*yYT>qy3;hN#%V3afw7 zh5mp_a8`EzH4=93L(085!RHhxAI;ADZ6**%QHtLv!pmN5X@D)OW3?=P)?kRb^GgJ8 zHjFTPvJ`)-dMtX`hc2P@{X||svP85U(q+V*D5VKcqh#zbltr61erstaB{Lc5Y`iZ) z-F(4MD!~~ykL?t_Lw9}C?nJ9PPJD|OQhIb4+Iy0;32?&+H=Z21)ae->{`C#CKlEM` z*XyoKGm|LI*|eCLa7Ul6HYpF56_4hN7Ja_xdVh@qLOa`4iz;vsM5C`3M6*qGshZ9Z>zjnBZ#|WA`w+6UT<}4RIGX&+`I=}-VykV zh8SvyH4v-&bs;YPX9^izfEusV3c@y0oZp}Kg(m5bwl_tf5S~E+feSvkjX|pfDhD{% z=hA5@aQ*)k#2Yy+48P=wL~Z{otK2T6q}fk-rz$kN&%`jK^qk-w@P{cb^(j=jd{St% zSnoaQ((=QC0tPiK>v{Wi>P?#6-T4l$7M_ncPq7xE(uW@hD?15^9-y}qDTlhNHX2L( z+29Z3%`nLd?yYXsmXV(RN^bo39lV~4mm9*iCk{MQpd}Y@NXb{k{UY`?-F-X^^+W!? zme=g5KDH{b#X~dTa+L+0;fhacl&m1Z$NF{V`$?YuBrXKKm&yD!PDysJh+_F6H&{zt zBOJ~L}dj-j&;^yEe^r?fqfp;L9!wBs8V=&h2vhwrgU@r;l){TM1qTif3EPQZ4TQ3{Mw@Eam*@^I zR5bW81O^2$xXzJ(0*Y}!MFOIhmP49%5w1RWh}OlfCob$Pc6eU|+e-tq(SOEzC!Rf# z=O!|L>3*#ZBo>i8PX(5~Mxdub_0svDCs|QsxY4wh70*vO5c|R4!-*mPxfCP;&|bno zPkz4Rk=Xtq9H0|I$co08E3yRy{I`-DeME#m_i_*Zx$YPv7$(<94uVc`y4^bZd^E;ljDoB*NJe6thR+iBCINmh5@ z&BILw<#3JdOV~i@vUw@<0n4{M{yOrkcWkBx7~YcFiuPh(okNVTDpX3X1rBHM)xNgW`!l$PwQ76^={XL-tKpOS4!>hqa-N4nzx9tV-J ziyL{W2i)CO98{Vs&Az_^FzxMkd*Y}3j=Q9{A5R=q?+}TJHBGRQDa)0%IY@M!SeG7# zTr~g@N2AWHiD%1kT z_Igcg=<}x_&RTDU*-7jP+1NttCbS`vG{*yx-wYGE@=jU%@5w_ZH;B@I&_9UoSRmr3EB*DTgBxE0p=YQ2PNqif?fv zg60~W0419V`42RZ%D~l}rAk^OwTUx0erOFh9MTAM;`qsn=iegrZgtUBw}W6+&Bfm)e&f*(Sx-;U9eW!Be+}D(|@RY@9|0Vl-!K8 zpdBR);m5lt`Vt7qa;QY5{8As&^B{P9VZn(8z7;&BinU=ldAXjru?@+`bhmRQ_Ir#T zJNON$EN+kBa87m;?4AGd3l<{fEU`Vf&0qP}qE~;onh=~hWer@<6Y@{Cd>^7f7e|od zGY-PgVll`XFFMIfqI(gUiaRxiM73l>`SN}^(m&J>+3;RrmGytM_sE$?-yHcIl5Fg+ zhTrT_XziI_boRp;(r->oF1CX3_yunN@v*?=5q>Z7)xy-CEw02Phb#&amhp>9O=Y+> zKjC(CyCuOP3XCe&9InHRD)Z;gcs-dI-|0U}`G(_xcF%di68H_%cFrFrhaHe4tZ71q z60BTF7C(Hd{1RpTcfTgS^R2o$5|-c1qzw<;PIKlxULN2?|-~x+u-y(PxfM#fpmba%*AtCGs>8IKC{oT_I0yF}U0z=bGwDvcl zU3;tbwsZnj#g(bVF%qbH)p0f&WoR^n`y!> zi!z2B7S{!V{GS>V)=fc@WP;kNU}#{v=wW2T(Av4B72r%W6md|uc(oTN^FVGzsVmk=5 zS$Fs>#VNb98^@ICxiY$Y&HwnI2dv|dYh8utLM=f;14aowED={sB*NBQlvD_7-+m}! zvUuW%C;cpQ$4ApzW%B4d@3LKhma6n159>ec19y#3*%+ed%!wTmp5>>BaqyV}-U1`|@(f9ebl4egkf zrSOFc4)yki`V)ht1|#~5$be;;{ra^={1>qgF>$oN-N6cF8wZ2YB&VI>e)=Z&fr~K` zC`sZr%FJT#ng*viy6i6sRT7bsH(|o~BX6v^kBG&r@X2#CUM4CNf$kcxv9!P5t)if> zYN#>%VW3tgqekddOYiZq`#zp=#|oH270;Uj0?EE?4#+q;P~LEow&%!`!|~|lG=c`r z)yh4Y_3KbC!mVV}u&7Aaw!jS~x8N)>*w7@nb$bCzDrrnFD_GBM^H&;u-4`>iz>2b( zbZ&*BV>M+z8131*W3(+rS!HVnyDsz8YTh3RP^PNoJF}lh$47`m(dJNr%4<$MkT4rm)57{E!2GPX0|+gw^GxnAR6~N)01@ zRf-U@oZ@cNDJXj5YQ(T|T8B~&Qzj~v=zgzE{0X=7$sB{R#3YDN`re=kKYqCq8U5wN zHc$56E1EFD9;szyZU?(GS_&uf#^d5uX-rkA8&l<{x^3!vYAvXtpx55 zrd;e4ww8vwmkQ&w)+PL6kX(YBhjO8iiobC*m=CWt(G$fTz66SSCOmT$RN4itzBS#3 z7QK%TeN4=x{fRRrIOO_BDZUaCCZAu3`QiJRdHS;WIvh?+On@g3~9ElNGU( zl|m)nb}qsi4!o-XVl6fNBR}cFxI(#Vi3-D{t|+$;6H`r({$;ThUqF@r$zKV>F{#{3 zC9#xf>is-QQB|_o1Xu_#zCKjErG3Zcc7H-j zOY6O;l{OiU$&f;)M=H&A%V@{*;0<7#Z4du`Htb>(heCC5;Vp9@W}td2WZx%yn_WZ4&Auk;G zNJ~#24IakxwV`m=;bL{Fg@!c$zrPGzfvDh*y_%|}HC6FYhkXLt$;p_bS_GL6#dmKF1sM}`@m@NjzX??eBp@JC>^4+?WIdSAiE5d zKl15U<6mi3JDB5RJ?4jr{$(`wp$bvV@STVS>u%PZT9@CYmnaDhn!eLe(klrW4xZs- z??U4;xqd??lPbTT~3C>AJD8Q+wX|?g+g@#_2zx%g4u; zXn>+R2pUrvH>5&?MXSRUlC{3S`1@>?WIZ)3tH=%yd4En&X#r^?3td^Jf{45rxlQO1 zaQ?-2rLc1SnYsKuWb#r|MifMx0Mj7GKe%r|P(n;B#K#9p=_ObWI8n8lG~CJ^U}a~| zohe>^T#Tf*qbG(Y7UGXVnFBGA19k}KH^)3s-jW_^d5S*5vD3x8l$x$pF~p2W#UwJK z*@?t+cl3&}6J`90s!h-YrLbap_Lsy(E`5jY!9Cg8)bL1EjK(NX{jasUFnMh{Hus2R z6`&=rb|ffZP=mL1Bwabmd{QTsFX+eSZ|q7!tzHo_E2CmCog)XH7O1NvPb7*TC%TR- zf&E@GN|`G*8t9TsQX+8-Af9}Zlrja}CMM5>Vk>$v!1&-~VrUU>m%bB}LPIDX#h1)! z9I^^xR+MjVcT-!Az?Q3Bnb+(=gRs4b|xlkcIO}To=?+9FtYk7(%(HN6Xz=2mk;FE zaM#SEpc5t-1h%hE2o|Z8$PF5TR>C!9vo7xMt5{~aEPut@dU@5RZi;o6bYbm&lKoLE zekucyEL#6a9Sd-be>;{)74t->}nKET!)5D0Lb3mr9&O3lWsyA67>sj!sh@hSg}x&3*xW4eur5FAgbOfj<=c zb`AKr6O15*ic!>6r6N;~ayKAsp!$x+A}zwd*R{ozC%P^#qwxo1S6UY^QwYNFX?6xJ zG~AUGGvyE7A(EB;`C2EAc=tFOOROoT>RJ<2N}fAJh5!Z8AP8)aeSz>pJu@Ma>P`rl zr~$d&ib~Zg22@HZ&-gDqclb*~uv46)S*!2*xBnEITUr#il&r4Ht0Qh`Bww3ZPn4+( ze>9XKp_Tcz1(BWMWs>B*(L}lrEWT6e7=|f6m& zQciE?qypbK1S0SsR=Ra-Xbn$Tv6w7-{X>vNx!qD{$)W&60$u8O@&uZiuplBDs<^!b z&UI47!3^a2PN!_1YR5lF1(0IFo!+&hj0{Q|{Q3aIe8HD*>R9q=e}5z(eFc<%tI2)k zTvnGFvsM?2f!<&{ej9&hka1O=&FP~0U-3Z+qolrbK*YD4SJod_Ou${9NTp@JEH2|R zIdChWWQIL{iStaW^%4s_gr)1hoemjUGtsa!3Ms7wIOTun4Y9mt=m&V(>4=2xln`J( z*@Y=AUfc%^3=e<%q8=Sb2%~>GH@9tVodq`4WxlpCU-ey`i*XX*WMrh=ZYkq%+s4y$ zKIFnQxjfIo>$|Da*Sgc;7q8FqHxAWApO*YGC_60}eqeQGXj;YL=DtTE3%k=xD0jC$ zlO=3dqwkz&U9H>Mc5DC~Gy=n(LV2wnBqs6n1IZBV&Bgr=)06JCeiU0&-iY50801RA zSk84+cRJ@4h&ZBZq0S4%Mia}N%<@K4v#o;&M+<>aXO0y}cHD8;B{Mza0$~+E^A6PtENz0H?$l04sg{(7l%dTRJMWbeK76&Hweg)&nRsy!WFJc3JcW@Z|(77`@=8K zd;4buYL2+X4G!AnSV3QB+0dc>sXZf~%KtcvQa%bc!2#YniIfr(`&a>Yb}c0;39j16 zTcJMurrn6%aipM>*fnE!BdWax%5q_h~?*2U7HV6%F0XIx`qY79^G z=qeu0+)MqL%+Pzj8=mqZ8o5%$Lj-=m3j(5Hvy~;To7Nr=rCfj0X7?! zmDGy`-&Ge0LY@0UWj#8PB9WpRmu(q@Mf=p%`aj_y2 z3G;eLl~A(4iuT=Ge=~46p72&E>CtNZUKo=f9&6*3jfrtOsOs%mPT1d)Jy~u`;`XS- zQ&{Qg@!{7bz&p!ql=Y-IZ6QXEU+?=KA8YhUBizWwMlysWb&c=6@YO=BXW*P<)fLE9 zv$Cnk`` znWxcwE2R%vGH*{+`li}Fup_eBZ(kw9_>NDvXS#P=!NK0L3&KbndQ!{Nk**m+g% z7J1q0=Ei1k#AIYdRAuofsScVe0%LeM0FsyaH)~f6OV;_sp91kmon`k0YWMWb{iy8X zUs21tV@TUad*T2yt>A9H-UT*xGV`Y_8L=ooNr{X&Xw@yjUo2LJASZUNSOG*lawjKh zs!)77M*K_<-LQTwr6~5m>T(6~v$xl5TsYM>0J5NdQqC@^lWOecq7O6}g)c2Di}=;= zvtJJ{urP}!d5#@3809LJ0ok=)Uw2~B-ruy*!2RZuAHu*2mR0r*PoC{I97jz};{Tif zE%T!|%%YyU&>v-O&7zh%4sRWKS@c_eS!i4x5hVNxkVL|1P*X(ujocjHJGY_3<#|F5 zps5$@yN(zP)soy2ywsP#(A@Rg?PtZhk@j!;O-mz0;^K@Ow~flw4sS-xR9>` zkPunVkwhhe?Ir@n6~E~#7MQnq&$``Pzbx=E(T+w}r*8t5FxOD8=guq5!6jmFs2N&Q zSzd3Vj*gB2bckSJV282M-eqNG8I)a|oLpQZV{P8cnVECOj91<92FCe>S1_oS6W?=P zMpu8Zd7tw#4#3i3MI`@J(pNx1+ub@kQZ4al2#wU49n@y4_JW^CZR(#Lo6Up{MWTTz zP@n}qD6&Bn3-L1w;t1N*uUg1AsyS5a{W(z-RZ@CTtKmRs*@j=~gNG2{-xx7HErTMI z)7+f=^5U-EiqghkXYuUVy6KqKjCD+hZrOnxMdX!DF^)Kh0EKn&7x*fa7@C}-X7jk( zI-#Jvd1Gq_2csRUuIVx-d}2|9sAj|CJV3liEVLcn68{N}J_RnvNS= zQC`+PT{I-}Yp=c01V9S|u~7bq2p=VPIU4Wo>2*!9&mpy4SA<;b^p-A0y_)zgru7Vo ziMQkH&p&e{>Hhpmb0RgKJ)PS#LE|6nzZH&;H>B7%AU62`0f~IFqwAE^Xlpt@M8)|h zxDDSkl3bdWjj?yxHPz~%Q~Hm$YQI0LvOv_Yo%4pW$Hyu!*0hqo(%T=!LfGlZtu0NufKg7`+ji-8;U99 zG)=0cVgR_dju;_9!(b_wc$Cazx!$(;>4|)|0}e)^p*Kv9mcZ;77)YqTfXdC)GiB_I zDdgYE33n2jcu=NF?(!~_Ov{};BB!R_|J(B+C(;3|3rZBmvsko&E_58B<6lxqZcs0e z$U5JAKK3?t(bJ$GW;g)^rJ_JTNs&~h{@#^A|KPw(zNoy0h6cqPYOMR+H<9n}0>5Pt zDsuIHaU|JFK8Hq~19&|8>w_ty(Rd0}M`KhW9iUVDWThcYG!9Sq=nu)>cf5Z@wkt}W z9NSWh=$1VKU|fRy9p%X-cm^mEU@H@Btd#-)LW44SN=>D{-H9_Z zH!gmzSCF+>Zn#S2ieF!JYm{b7L~s;ru06kN!6EtPyVZ9ydiSGgJ6ia_=N=<1rFBCA_XK0CCfhlRnt)ZD^<{f;9j zCI$nvB5E{S`v4)tYC3vDs>AW3yoP!A^0KH#%x!XSF?I9Fy0_cD$^U(rjqAZGOg70? zXaNoISYn_xlJB?YX9`hK9QJDbyqRFXJR)vx?s|*G?^ae>SdRrK+l&Z8KIfg!k${I) zVp}Fah%;wqFc?KSYdhx#FpP2NTFFYPY~D8!_!pC*0{Tbe#p~FJ>w3iSk;Z;eqy3#z zncM66o$Z%TGW?2-xALCODM`#iS1)b%v!Uj96>6jKt5MOmbGtt7|6dEhH<;%Fd4|If zsP$7LI^O-RDtV|hBP3rRRd>PJW~_P9xxI{GBn_SJe5i{pLr?G&PcSG(K6!28U0Z98 z{^0(UWWMaDp88Vj--Y-*oZ06;rzMx)!t3E*RyX*Yyx_L&ngF*uNw!@KMrS6AA@7=< zL-c)L;-c!S0_CNReFMYIRj>x#Uj10ME=zX3#m~hiY_@2#&#Ne5{R8qC9T>dgLqj|} zC6h_?EqnMHeeGVZ)0GYgE3u(ztexAGo?@^%{o+$}<&Op2Pd}l##GwiH;@3->-Q7qO z-0w!K`sQU2?ZqMal`VCqmSY41LwKvfvhf63wiWsaOK|FF<2TaEA{eFuMX4Duvy+r_bZAG z_iIU@rgON`U>2YY;Q3z8z25bJxgt0u$Y4JlJ<9lvlgRR*a|HatQMTDy|0iss$7pQf z`Gv8F5uv(B?z)Y?o{Ip~Z458P?rw;EeT;xfC9|#ji2C^Y)=+khG~I^CRL(qryE;R9 zF_g})(ED6;e@4V$%^HUv+^@Kh2XiGTsyR_~eZRx-q665g2~SEF4KKE(FTn{<5*Oy0 zIzmdGa#=TM|M>h57H6e1>jVw=qO3ckyGB;z>>qkWQ+!`1lep^38M!h|E6DRV zF1-Dex18cnBGO(88lM;$aUW2k*7n2v{llzC+|eb1mfN{tqaL0yr`L``1wzM0l_rzS zXhPbtx^RoZdX2a4z>B2za{M0&36&Ym#llcX>q>;jBBP=X08bBrqQ%Q&tFF&LW>(nU z{ZwKq<7#}CO1smy>HQf?`sDysUkHe>gHNYZ@`Aos+w0Mh)}af>!3yVvX!)J*jImY4 zq-2Wmdhbahdr?EsuM;AX)%)i!YCIQ6eH^VspC|B!_q*fqmt0;xuM94aIsqJA3yH8; z3bX~HFDRhO`p8hy@)V-!@|V&P^x@Hj%ixQx+`%7T?hOC<9XuG%XcV59h&@*}&D0qw z>w;&9IrNZdq&a2VajbF^bxhj*owqpQ(JPSD|AOlt#S4r{>a?%he^CXEfBi0KySKH( zG7$ptt1}O5fsVD58Z|mQsu%co1fesPJgv>gK2*_?E|>zu5s!PBmn$G5l2Vnp{T5E)jGhzZ#Nq7F)%hpe!4mO_yedr z_>Jhx54^p(u>d;&1O$YFn^&H26r!>%?vJA5UmD0xd9@e;bpOpxz2WatyN)d)7`d6U zu2{n#F!FY1I?}kK+D~c1c>su(A3zZ%0}xFBL~tY~*bl8t>uiPw=X#>{hh0 zh7P_e?nWGko@1T!!gadTp=j0yS93wi^B%b2fd2@@_sD{vS2Zk$wBh57z?fz?4t3@V zgUP8fDC|2{X}FO5_N|yv8U!9uk({@c4&@3mGzN!#*U2R3MzSj}z0OZ_jJR}hn3w4H zvTk$&l}bU2L5hT{KKTPPJ$GSk7e^LLz(=9~&HJtfWLpM`AIip^I2>Pu&bd3xB&&lX zLbj6Nc&`8Yi>|qefPo8#=XxBRJwUPy>dO9-+g<+=rhkp6A1s zDgIpSKv?B6E&oNcBb6oh$N9|Ni3%NvZh@3xgiX$l3{|BTP$mi=l>(nRz8$U&ry6U& zT~H6lyt;C`x%}kWe8rjl z%0hD-@q_K}-%q4Fdw!H7JxRg-J%YqHdFN4eDV^?k$EzKanmK#nV~RiubFP2R#i8!A zghw7Fnh=50>cKdd1MJks=4WNm-W~>w3Omxzk@RG-a@7c$kJ(8~Dv(S<$~1y>dW?Px ze3XUWYY?ogtO$#(Ou!MDBv(ihP;7m-cg^tlH?&7bX&M$B4CVRk0Diqcp?3;^Y};)q z*w~hT#rk{k{WjfwgW8J z`?H-MZr|~_!?mi4ii?A{?n>~S)|q^PBU*g9x`xKMvbL9{4n|Q7O|Sw-Y%~mE58cup=T2h7YfD(| zU^UoyCRQ|1b@4=##zvRTR#N~g>#}u_m-~YSok4+Cu_q?FncO|e>Iady&omZ&=lXT_ zFYRy**Uj%6jF#JQtzivoDPjZ94y0ruMvkdU7dFfe`rTD&%=bLwSM5|V)aenCP)!cr$0 z?h1?-6?p!RiIeA-FAHa<0{Y6zIrST#P$IyW*M*Vl@4)}J+h{N>H%0KvngtO2qxkrO z_XnQcZ?Df)CRo98(W5b5Zxnki*}YuEvC+ag5ENY(&!wl%PTzY3GQ{t?ypo-ag{IDY z+FQ>k>BF#Qo#d)5ypK10-ncw>C$hNz65S%Siag}f?Z=kFX1gs~r&D5)Wo-@(=E3Ah zx?9)p+U};~X{_6{mG2x0h9ySCuw;-fV@dmCjgu{2?V%BE%}hXDSPRY+)L8YA{=w}% zT&({}L{sc*vLHA% zsy1>wA6ftF%|WL>%&4WRtmc}vlj1OUZI~Y>Up<^{r2>9OuHD9*ctpBG=qx*aZmL_@ zn7zMpNhSRJeN1?JO_G5Y7bGm>tezo}z1r+i!c|I}i?=$|viYoh{Og^R1J z*4)MZW}ABkOMr9y%87!Ge!VSD!*Gz%R7Ovj-@1XCx{|rrU_7I%R&UG)oF)LG6Oefs zp<(6&_-i5$62$xkbe)5ou+GYWpLjqop5c zE8F-hsw%W*h`=qIxBTYWkI*ptDI>qICjHj@E6a~DMa{8R#f&#zO(12)VAJb2p8Uw( z)6+8#IN`Bf5?Q^U56P*UnA~3kNt`lAkHBmEeoGZ<@5q5t-SJN|qWuXjKE;7a2|YBS29=accTpJSq>u25dKp`i5iN!@PhL8c4U;OkEW~C zw!PWTrT1p_Dai~D1h(2#bUA8q!fi)`sAF1=<)i`oX1Lz#ppTj?!{&xGmkX|!*^#F8 z_9vl(t#pB{aCX=R=lR4fv8xNa%ZC9RrcFf}@abz^=%m&g=5?Eoq{;sq~u1mZMd8=&|*l-I>IGCRe_5 zgMqyrqFP(&M|oIA0yTwHEwGY|doSx3p$L}G{Dq8Q^!r8vC~rn0oJJiEO=J9P3)U*F*+dE7r& z9fz2#4`${nC|a)k5vM^tkc$fmjjZ_N!qMynZ(~6drQo$cj#)UB95Q1}<%IPMpy~jb zJJm!wjBgK5PqrtAF{x%U`^Z$OcX+COZ$t8$oK>4umtx<$rZNZVn?l-|TJ+d9(0xIA z?4!O1o7;l-nBy2ogoQp({~%D;pZ{?v-j*-THDRiu8WTfDi1AZux?8=|0OYa+2vbph zxLT=vZIuZp$0f12$#<#D4;K&z9`~vCU#p9VH`$KznIeGbyte#$Q z28YuOJ9QHsmB3w-Rh z6k(1hmBnh(D9bZ++5M3bVc^YA)da{mo9QZuE6ITdLVe`BsCMBGIwg*GT|A25es$X8 z&V<2PQR(f?EQCimqRpaJLc@+6cNx?%`H;D?0EH@EYd95DO3KKp9}O2c;OgIh{2{vv zTokq2Bk&T^R-m5V_YHuBf`i+eE0G7D=o4TXqgfIP7$VbIZE$X+Fckfh6Y9~X($LwT zeE&wq59Guc@`dh+X-{b*Q;hyEXfz3blN?6pW5ULXjQq};D7vG%%5}-u=<%qnRA>wp z{BQD|%Y|%$Y4N=ow>Oa1ggAXpO^B%ZE-rMGB`^p1X?9TY=JAWK*;dYN99y!Zul1s1 zdtDvP@tLLo_Ioqdn!voCvM~sS6x7ld3TXJsRj-jh=-bB&6^hjAi3Um)S`+0go8?!H ziT$rQAXbqcvbrUIf4jAHPP$?eU9{B*A=#3VlV=11aV`GRJ>yp36#B!MeCj)DiU%IQ zY)Db_Uo2S`y`^y)OvgUTZ9$bTH)v#|{IAx;`Nv%TPy#>|!+)||PjMTbs*MB_y#X?F z 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

`applications.`

+ +**Purpose:** Holds your application definition. + +**Name:** The name of the application. User-defined, usually identical to [`applications..charm`](#heading--applications-application-charm) + +

`applications..annotations`

+ +```text + # + # annotations: + # + # Affects the GUI only. It provides horizontal and vertical placement of + # the application's icon on the GUI's canvas. Annotations are expressed in + # terms of 'x' and 'y' coordinates. + # + + annotations: + gui-x: 450 + gui-y: 550 + +``` + +## `applications..base` + +## `applications..bindings` + +```text + # + # bindings: + # + # Maps endpoints to network spaces. Used to constrain relations to specific + # subnets in environments where machines have multiple network devices. + # The empty ("") key represents all endpoints and can be used to specify the + # default space for any endpoint that is not explicitly bound to a space. + + bindings: + "": alpha + kube-api-endpoint: internal + loadbalancer: dmz + +``` + +## `applications..channel` + +**Purpose:** States what the preferred channel should be used when deploying a non-local charm. **Note:** Charmhub charms expect `//` format (e.g., `latest/stable`). + +**Example:** + +```text +channel: latest/edge +``` + +## `applications..charm` + +**Purpose:** States what charm to use for the application. **If you're defining a public bundle:** Use a fully qualified charm URI. + +**Example:** + +```text +charm: containers-easyrsa +``` + + + +## `applications..constraints` + +```text + # + # constraints: + # + # Sets standard constraints for the application. As per normal behaviour, + # these become the application's default constraints (i.e. units added + # subsequent to bundle deployment will have these constraints applied). + # + + constraints: root-disk=8G + + constraints: cores=4 mem=4G root-disk=16G + + constraints: zones=us-east-1a + + constraints: "arch=amd64 mem=4G cores=4" + +``` + +## `applications..devices` + +## `applications..expose` + +```text + # + # expose: + # + # Exposes the application using a boolean value. The default value is + # 'false'. + # + # In order to use the granular per-endpoint expose settings feature + # (Juju 2.9 or newer) by specifying an "exposed-endpoints" section, the + # expose field must either be set to false or omitted from the bundle. + # + + expose: true + +``` + + + +## `applications..exposed-endpoints` + +```text + + # exposed-endpoints: + # + # Specifies the set of CIDRs and/or spaces that are allowed to access the + # port ranges opened by the application. Expose settings can be + # specified both for the entire application using the wildcard ("") key + # and for individual endpoints. + # + # NOTES: + # - This is a deployment-specific field and can only be specified + # as part of an overlay. + # - This field is supported since Juju 2.9. + +``` +Since Juju 2.9, operators can control the expose parameters (CIDRs and/or spaces that are allowed access to the port ranges opened by exposed applications) for the entire application and/or on a per-endpoint basis. + +Application expose parameters can also be specified in bundles. However, as expose parameters are deployment-specific, they can only be provided as part of an overlay. Consider the following multi-document bundle: + +```yaml +applications: + mysql: + charm: "mysql" + num_units: 1 +--- # overlay +applications: + mysql: + exposed-endpoints: + "": + expose-to-cidrs: + - 0.0.0.0/0 + - ::/0 + db-admin: + expose-to-spaces: + - dmz + expose-to-cidrs: + - 192.168.0.0/24 +``` + +This is equivalent to the following commands: + +```text +juju deploy mysql +juju expose mysql --to-cidrs 0.0.0.0/0,::/0 +juju expose mysql --endpoints db-admin --to-spaces dmz --to-cidrs 192.168.0.0/24 +``` + +As a result of the above commands, the mysql application will be exposed and: +- All port ranges opened by the charm for any endpoint **except** `db-admin` will be reachable by **any** IP address. +- Port ranges opened by the charm for the `db-admin` endpoint will only be reachable by IPs that are part of the `192.168.0.0/24` block or belong to a subnet associated with the `dmz` space. + +```{note} + +When using this particular feature, the bundle must not also contain an `expose: true` field or Juju will display an error when attempting to deploy the bundle. + +This constraint prevents operators from accidentally exposing **all** ports for an application when attempting to deploy such a bundle to a pre 2.9 controller as older controllers would honor the `expose: true` flag but would not interpret the `exposed-endpoints` field. + +In addition, 2.9 (and newer) Juju clients will also display an error when attempting to deploy a bundle containing an `exposed-endpoints` section to a pre 2.9 controller. + +``` + +## `applications..exposed-endpoints.expose-to-cidrs` + +## `applications..exposed-endpoints.expose-to-spaces` + + +## `applications..num_units` + +**Purpose: Specifies the number of units to deploy. + +**Value:** Integer = the number of units. Default: '0'. + +**Example:** + +```text +num_units: 2 +``` + +## `applications..offers.` +## `applications..offers..acl` +## `applications..offers..endpoints` +## `applications..offers` + +```text + + # offers: + # + # Specifies a list of offers for the application endpoints that can be + # consumed by other models. Each offer entry is identified by a unique + # name and must include a list of application endpoints to be exposed + # as part of the offer. In addition, each offer may optionally define an + # "acl" block to control, on a per-user level, the permissions granted to + # the consumer side. The "acl" block keys are user names and values are + # permission levels. + # + # NOTES: + # - This is a deployment-specific field and can only be specified + # as part of an overlay. + # - This field is supported since Juju 2.7. + # + + offers: + my-offer: + endpoints: + - apache-website + acl: + admin: admin + user1: read + +``` + +## `applications..options` + +```text + # + # options: + # + # Sets configuration options for the application. The keys are + # application-specific and are found within the corresponding charm's + # metadata.yaml file. An alias (a string prefixed by an asterisk) may be + # used to refer to a previously defined anchor (see the 'variables' + # element). + # + + options: + osd-devices: /dev/sdb + worker-multiplier: *worker-multiplier + +``` + +Values for options and annotations can also be read from a file. For binary files, such as binary certificates, there is an option to base64-encode the contents. A file location can be expressed with an absolute or relative (to the bundle file) path. For example: + +``` yaml +applications: + my-app: + charm: some-charm + options: + config: include-file://my-config.yaml + cert: include-base64://my-cert.crt +``` + +## `applications..placement` +## `applications..plan` + +```text + + # + # plan: + # + # This is for third-party Juju support only. It sets the "managed + # solutions" plan for the application. The string has the format + # '/'. + # + + plan: acme-support/default + +``` + +## `applications..resources` + +**Purpose:** States what charm resource to use. + +**Value:** Map. Keys are individual resources. + + +Bundles support charm resources (see {ref}`Using resources <5679md>`) through the use of the `resources` key. Consider the following charm `metadata.yaml` file that includes a resource called `pictures`: + +``` yaml +name: example-charm +summary: "example charm." +description: This is an example charm. +resources: + pictures: + type: file + filename: pictures.zip + description: "This charm needs pictures.zip to operate" +``` + +It might be desirable to use a specific resource revision in a bundle: + +``` yaml +applications: + example-charm: + charm: "example-charm" + series: trusty + resources: + pictures: 1 +``` + +So here we specify a revision of '1' from Charmhub. + +The `resources` key can also specify a local path to a resource instead: + +``` yaml +applications: + example-charm: + charm: "example-charm" + series: trusty + resources: + pictures: "./pictures.zip" +``` + +Local resources can be useful in network restricted environments where the controller is unable to contact Charmhub. + +## `applications..resources.` + +**Purpose:** Defines individual resources. + +**Name:** Application specific. Cf. the charm's `metadata.yaml`. + +**Value:** Integer = the resource revision stored in the Charmhub or String = absolute or relative file path to local resource. + +**Examples:** + +```text +easyrsa: 5 +``` +```text +easyrsa: ./relative/path/to/file +``` + + +## `applications..revision` + +**Purpose:** States the revision of the charm should be used when deploying a non-local charm. Use requires a channel to be specified, indicating which channel should be used when refreshing the charm. + +**Example:** + +```text +revision: 8 +``` + +

`applications..scale` +

+ # The application's existing units are iterated over in ascending + # order, with each one being assigned as the destination for a unit to + # be placed. New machines are used when 'num_units' is greater than the + # number of available units. The same results can be obtained by + # stating the units explicitly with the 'unit' type above. + # + + to: ["django"] + + # + # :new + # Unit is placed inside a container on a new machine. The value for + # `` can be either 'lxd' or 'kvm'. A new machine is the + # default and does not require stating, so ["lxd:new"] or just ["lxd"]. + # + + to: ["lxd"] + + # + # : + # Unit is placed inside a new container on an existing machine. + # + + to: ["lxd:2", "lxd:3"] + + # + # : + # Unit is placed inside a container on the machine that hosts the + # specified unit. If the specified unit itself resides within a + # container, then the resulting container becomes a peer (sibling) of + # the other (i.e. no nested containers). + # + + to: ["lxd:nova-compute/2", "lxd:glance/3"] +``` + +

`applications..trust`

+ +

`bundle`

+ +If set to `kubernetes`, indicates a Kubernetes bundle. + +

`default-base`

+ +

`description`

+ +**Purpose:** Sets the bundle description visible on Charmhub. + +**Examples:** + +```text +description: This is a test bundle. +``` + +```text +description: | + + This description is long and has multiple lines. Use the vertical bar as + shown in this example. +``` + + +## `docs` + + +```text +# (Optional) A link to a documentation cover page on Discourse +# More details at https://juju.is/docs/sdk/charm-documentation +docs: + +``` + +## `issues` + +**Example:** + +```text +# (Optional) A string (or a list of strings) containing a link (or links) to the bundle bug tracker. +issues: | [] +``` + + +## `machines` + +```text + +# machines: +# +# Provides machines that have been targeted by the 'to' key under the +# '' element. A machine is denoted by that same machine ID, +# and must be quoted. Keys for 'constraints', 'annotations', and 'series' can +# optionally be added to each machine. Containers are not valid machines in +# this context. +# + +machines: + "1": + "2": + series: bionic + constraints: cores=2 mem=2G + "3": + constraints: cores=3 root-disk=1T + + +``` + +## `machines..annotations` + + + +## `machines..base` +## `machines..constraints` +## `machines..series` +## `name` + +**Example:** + +```text +# name: +# +# Name defines an optional name for the bundle. Used only for Charmhub +# Store and is omitted for other stores (charmstore, private) and local +# deployments. +# + +name: foo +``` + + +## `relations` + + +**Example:** + +```text +# +# relations: +# +# States the relations to add between applications. Each relation consists of a +# pair of lines, where one line begins with two dashes and the other begins +# with a single dash. Each side of a relation (each line) has the format +# ':', where 'application' must also be represented +# under the 'applications' element. Including the endpoint is not strictly +# necessary as it might be determined automatically. However, it is best +# practice to do so. +# + +relations: +- - kubernetes-master:kube-api-endpoint + - kubeapi-load-balancer:apiserver +- - kubernetes-master:loadbalancer + - kubeapi-load-balancer:loadbalancer + +``` + +## `saas` + +**Example:** + +```text +# +# saas: +# +# Specifies a set of offers (from the local or a remote controller) to consume +# when the bundle is deployed. Each entry in the list is identified via a unique +# name and a URL to the offered service. Offer URLs have the following format: +# [:][/]. +# +# If the controller name is omitted, Juju will use the currently active controller. +# Similarly, if the model owner is omitted, Juju will use the user that is currently +# logged in to the controller providing the offer. +# + +saas: + svc1: + url: localoffer.svc1 + svc2: + url: admin/localoffer.svc2 + svc3: + url: othercontroller:admin/offer.svc3 +``` + + +## `saas.` +## `saas..url` +## `series` + +```text +# series: +# +# Sets the default series for all applications in the bundle. This also affects +# machines devoid of applications. See 'Charm series' above for how a final +# series is determined. +``` + +**Example:** + +```text +series: bionic +``` + +What series a charm will use can be influenced in several ways. Some of these are set within the bundle file while some are not. When using bundles, the series is determined using rules of precedence (most preferred to least): + +- the series stated for a machine that an application unit has been assigned to (see `series` under the `` element) +- the series stated for an application (see `series` under the `` element) +- the series given by the top level `series` element +- the top-most series specified in a charm's `metadata.yaml` file +- the most recent LTS release + + + +## `source` + +**Example:** + +```text + +# (Optional) A string (or a list of strings) containing a link (or links) to the bundle source code. +source: | [] +``` + +## `tags` + +```text +# tags: +# +# Sets descriptive tags. A tag is used for organisational purposes in the +# Charm Store. See https://docs.jujucharms.com/authors-charm-metadata +# for the list of valid tags. +``` + +**Examples:** + +```text +tags: [monitoring] +``` + +```text +tags: [database, utility] +``` + + +## `type` + +## `variables` + + +```text +# variables: +# +# Includes the optional definition of variables using anchors. Corresponding +# values are later manifested with the use of aliases. An anchor is a string +# prefixed with an ampersand (&) whereas an alias is the same string prefixed +# by an asterisk (*). The alias will typically be used to specify a value for an +# application option (see element 'options'). +``` + +**Example:** + +```text +variables: + data-port: &data-port br-ex:eno2 + worker-multiplier: &worker-multiplier 0.25 + +``` + +## `website` + + +```text +# (Optional) A string (or a list of strings) containing a link (or links) to project websites. +# In general this is likely to be the upstream project website, or the formal website for the +# charmed bundle. +website: | [] + +``` diff --git a/docs/reference/files/file-charmcraft-yaml.md b/docs/reference/files/file-charmcraft-yaml.md new file mode 100644 index 000000000..22e760730 --- /dev/null +++ b/docs/reference/files/file-charmcraft-yaml.md @@ -0,0 +1,1356 @@ +(file-charmcraft-yaml)= +# File `charmcraft.yaml` + +```{toctree} +:maxdepth: 2 +:hidden: true + +/reference/models/index +``` + + + + + +> {ref}`List of files in the charm project ` > `charmcraft.yaml` +> +> [Source](https://github.com/canonical/charmcraft/blob/4f9aa82b5e36335301d8bb81b74e46e8c7ae5762/charmcraft/models/charmcraft.py#L106) +> +> See also: {ref}`How to configure Charmcraft ` + +```{important} + +Starting with Charmcraft 2.5, the `charmcraft.yaml` file is the only `yaml` file generated by `charmcraft init` and the only `yaml` file in a charm project that a charm author is supposed to edit directly. + +(`charmcraft pack` will use the information you provide here to generate the {ref}``actions.yaml` `, {ref}``config.yaml` `, and {ref}``metadata.yaml` `, as well as all the other files it usually does.) + +``` + + +The `charmcraft.yaml` file is a file in your charm project that contains keys that allow you to declare various types of information about the project (type, name, summary, etc.; how it should build, and what actions, configurations, integrations, etc.) in a form that can be used by Charmcraft, Charmhub, and Juju. + +```{important} + +If you're starting from an empty file, the only required key is the [`type`](#heading--type) key. However, depending on what value you set it to (`charm` or `bundle`), other keys become required as well. + +``` + + +````{dropdown} Expand to view the full spec with sample content all at once + + +```text +#This is intentionally designed to be a fairly complex charm definition. It makes use +# of every available keyword in charmcraft.yaml, expressing as much as possible. + +type: charm +name: full-charm +summary: A fully-defined charm, making use of all the available charm keywords. +description: | + This is intentionally designed to be a fairly complex charm definition. It makes use + of every available keyword in charmcraft.yaml, expressing as much as possible. +analysis: + ignore: + attributes: + - framework + linters: + - entrypoint +charmhub: + api-url: https://api.staging.charmhub.io + storage-url: https://storage.staging.snapcraftcontent.com +parts: + im-not-calling-this-what-you-expect: + plugin: charm + source: . + charm-entrypoint: src/charm.py + charm-binary-python-packages: [] + charm-python-packages: [] + charm-requirements: [] + charm-strict-dependencies: false + another-part: + plugin: nil +bases: +- build-on: + - name: ubuntu + channel: '22.04' + architectures: + - amd64 + - riscv64 + - name: ubuntu + channel: '20.04' + architectures: + - amd64 + - arm64 + run-on: + - name: ubuntu + channel: '22.04' + architectures: + - amd64 + - name: ubuntu + channel: '22.04' + architectures: + - riscv64 + - name: ubuntu + channel: '22.04' + architectures: + - arm64 +- build-on: + - name: ubuntu + channel: '24.04' + run-on: + - name: ubuntu + channel: '24.04' + architectures: + - amd64 + - arm64 + - riscv64 + - s390x + - ppc64el + - armhf +actions: + snapshot: + description: Take a snapshot of the database. + params: + filename: + type: string + description: The name of the snapshot file. + compression: + type: object + description: The type of compression to use. + properties: + kind: + type: string + enum: + - gzip + - bzip2 + - xz + quality: + description: Compression quality + type: integer + minimum: 0 + maximum: 9 + required: + - my-favourite-photo.tiff + additionalProperties: false +assumes: +- any-of: + - juju >= 2.9 + - all-of: + - juju >= 3.0 + - juju < 4.0 +- k8s-api +containers: + super-app: + resource: super-app-image + mounts: + - storage: logs + location: /logs +devices: + super-cool-gpu: + type: amd.com/gpu + description: Some sweet AMD GPU + countmin: 69 + countmax: 420 + lame-gpu: + type: nvidia.com/gpu + description: A GPU I regret buying + countmin: 0 + countmax: 1 +extra-bindings: + Ring of Power: null +peers: + friend: + interface: life + limit: 150 + optional: true + scope: container +provides: + self: + interface: identity +requires: + parent: + interface: birth + limit: 2 + optional: false + scope: global +resources: + water: + type: file + filename: /dev/h2o + super-app-image: + type: oci-image + description: OCI image for the Super App (hub.docker.com/_/super-app) +storage: + jbod: + type: block + description: A nice block storage for me to use as swap space + shared: false + properties: + - transient +subordinate: false +terms: +- Butterscotch is regal +- Cara is adorable +links: + contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont + documentation: https://juju.is/docs/sdk/charmcraft-yaml + issues: + - https://launchpad.net/~charmcraft-team + - https://github.com/canonical/charmcraft/issues + source: + - https://github.com/canonical/charmcraft + website: + - https://snapcraft.io/charmcraft +config: + options: + name: + default: Wiki + description: The name, or Title of the Wiki + type: string + skin: + default: vector + description: skin for the Wiki + type: string + logo: + description: URL to fetch logo from + type: string + admins: + description: 'Comma-separated list of admin users to create: user:pass[,user:pass]+' + type: string + debug: + default: false + type: boolean + description: turn on debugging features of mediawiki +``` + +```` + + +Read on to find out more about each key. + + + + +(file-charmcraft-yaml-actions)= +## `actions` +> Owned by `juju`. Used by `charmcraft`, `charmhub`, `juju`. + + +**Status:** Optional. + +**Purpose:** Defines an action. + +**Name:** String = user-defined action name. + +**Value:** Mapping = the YAML equivalent of a JSON Schema object, except: + +1. It includes some new keys specific to actions: `description`, `parallel`, and `execution-group`. +2. It does not currently support the JSON Schema concepts `$schema` and `$ref`. +3. The `additionalProperties` and `required` keys from JSON Schema can be used at the top-level of an action (adjacent to `description` and `params`), but also used anywhere within a nested schema. + +> See more: [JSON Schema Docs](https://www.learnjsonschema.com/2020-12/) + +## `actions..description` + +**Status:** Optional. + +**Purpose:** Describes the action. + +**Value:** String = the action description. + + + +## `actions..execution-group` + + +**Status:** Optional, defaults to “”. + +**Purpose:** Holds action task execution groups. + +**Value:** String = the execution group in which to place tasks created by this action. + +> See more: [Juju | `juju run`](https://juju.is/docs/juju/juju-run), [Juju | Task](https://juju.is/docs/juju/task) + +## `actions..parallel` + + +**Status:** Optional, defaults to false. + +**Purpose:** Sets whether to allow tasks created by this action to execute in parallel. + +**Value:** Boolean. + +> See more: [Juju | `juju run`](https://juju.is/docs/juju/juju-run), [Juju | Task](https://juju.is/docs/juju/task) + +## `actions..params` + +**Status:** Optional. + +**Purpose:** Holds action parameters. + +**Value:** Mapping. Keys are parameter names. + + The entire map of `actions..params` is inserted into the action schema object as a “properties” validation keyword. The Juju CLI may read the “description” annotation keyword of each parameter to present to the user when describing the action. + + +## `actions..params.` + +**Status:** Optional. + +**Purpose:** Defines an action parameter. + +**Name:** The name of the action parameter. + +**Value:** Mapping = the YAML equivalent of a JSON Schema object. + +> See more: [JSON Schema Docs](https://www.learnjsonschema.com/2020-12/) + + +(file-charmcraft-yaml-analysis)= +## `analysis` +> Owned by `charmcraft`. Used by `charmcraft`. + +**Status:** Optional. + +**Purpose:** Defines how the analysis done on the package will behave. This analysis is run implicitly as part of the `pack` command but can be called explicitly with the `charmcraft analyze` command. + + + +**Structure:** So far the only thing that can be configured is which attributes or linters will be ignored. + +```text +analysis: + ignore: + attributes: [,...] + linters: [,...] +``` + +**Example:** + +```text +analysis: + ignore: + attributes: + - framework + linters: + - entrypoint +``` + +(file-charmcraft-yaml-assumes)= +## `assumes` + +**Status:** Optional. Recommended for Kubernetes charms. + +**Purpose:** Allows charm authors to explicitly state in the metadata of a charm various features that a Juju model must be able to provide to ensure that the charm can be successfully deployed on it. When a charm comes preloaded with such requirements, this enables Juju to perform a pre-deployment check and to display user-friendly error messages if a feature requirement cannot be met by the model that the user is trying to deploy the charm to. If the assumes section of the charm metadata is omitted, Juju will make a best-effort attempt to deploy the charm, and users must rely on the output of `juju status` to figure out whether the deployment was successful. The `assumes` key is available since 2.9.23. + +**Structure:** The key consists of a list of features that can be given either directly or, depending on the complexity of the condition you want to enforce, nested under one or both of the boolean expressions `any-of` or `all-of`, as shown below. In order for a charm to be deployed, all entries in the `assumes` block must be satisfied. + +```yaml +assumes: + - + - any-of: + - + - + - all-of: + - + - +``` + + The supported feature names are as below: + +|||| +|- | - | - | +|`juju `

E.g., `juju < 3.0`.
E.g., `juju >= 2.9` | The charm deploys iff the model runs agent binaries with the specified Juju version(s). |Since 2.9.23| +|`k8s-api` | The charm deploys iff the underlying substrate for the model is Kubernetes. |Since 2.9.23| + +The Boolean expressions are defined as below: + +||| +|-|-| +|`any-of`| The sub-expression is satisfied if any of the provided child expressions is satisfied.| +|`all-of` | The sub-expression is satisfied if all of the provided child expressions are satisfied.| + +**Examples:** + + +````{dropdown} Expand to see a simple example + + +```yaml +assumes: + - juju >= 2.9.23 + - k8s-api +``` + +```` + + +````{dropdown} Expand to see an example with a nested expression + + +```yaml +assumes: +- any-of: + - juju >= 2.9 + - all-of: + - juju >= 3.0 + - juju < 4.0 +- k8s-api +``` + +```` + +(file-charmcraft-yaml-bases)= +## `bases` + +`````{note} + +`bases` is replaced by `base` , `build-base`, and `platforms`. + + + +````{dropdown} See more + + +```yaml +# The run time base, the base format is @, +# accepted bases are: +# - ubuntu@24.04 +base: +# The build time base, if not defined the base is also the build time +# base, in addition to valid bases, the build-base can be "devel" +# which would use the latest in development Ubuntu Series. +build-base: + +platforms: + # The supported platforms, may omit build-for if platform-name + # is a valid arch, valid architectures follow the Debian architecture names, + # accepted architectures are: + # - amd64 + # - arm64 + # - armhf + # - ppc64el + # - riscv64 + # - s390x + : + # The build time architecture + build-on: | + # The run time architecture + build-for: | + +``` + +```` + +````` + +**Status:** If the [`type`](#heading--type) key is set to `charm`, required. (If the `type` key is set to `bundle`, leads to an error.) + +**Purpose:** Specifies a list of environments (OS version and architecture) where the charm must be built on and run on. + +When packing in "destructive mode", the base(s) that match(es) the current environment will be used, otherwise an instance will be requested to LXD or Multipass for each specified base to pack in that environment. + + +**Structure:** This key supports a list of bases where the charm can be built, and where that build can run. Each item can be expressed using two different internal structures, a short and a long form. The long one is more explicit: + +```yaml +bases: + - build-on: + - name: + channel: + architectures: + - + run-on: + - name: + channel: + architectures: + - +``` + +The `run-on` part of each `build-on` is optional, and defaults to what's specified in the corresponding 'build-on'. And in both structures the list of architecture strings is also optional, defaulting to the machine architecture. + +The short form is more concise and simple (at the cost of being less flexible): + +```yaml +bases: + - name: + channel: + architectures: + - +```` + +It implies that the specified base is to be used for both `build-on` and `run-on`. And as above, the list of architecture strings is also optional, defaulting to the machine architecture. + +Be sure to check [this detailed documentation](https://discourse.charmhub.io/t/charmcraft-bases-provider-support/4713) for more information and the different possibilities of these structures, including several examples. + + +**Example:** + +```text +bases: +- build-on: + - name: ubuntu + channel: '22.04' + architectures: + - amd64 + - riscv64 + - name: ubuntu + channel: '20.04' + architectures: + - amd64 + - arm64 + run-on: + - name: ubuntu + channel: '22.04' + architectures: + - amd64 + - name: ubuntu + channel: '22.04' + architectures: + - riscv64 + - name: ubuntu + channel: '22.04' + architectures: + - arm64 +- build-on: + - name: ubuntu + channel: '24.04' + run-on: + - name: ubuntu + channel: '24.04' + architectures: + - amd64 + - arm64 + - riscv64 + - s390x + - ppc64el + - armhf +``` + + +(file-charmcraft-yaml-charm-libs)= +## `charm-libs` + +**Status:** Optional. + +**Purpose:** Declares charm libraries for Charmcraft to include in the charm project. For each lib, make sure to include both the lib name (in `.` format) and the lib version (in `"[.]"` format). For example: + +```yaml +charm-libs: + # Fetch postgres_client lib with API version 1 and latest patch: + - lib: postgresql.postgres_client + version: "1" + + # Fetch mysql lib with API version 0 and patch version 5: + - lib: mysql.mysql + version: "0.5" +``` + +(file-charmcraft-yaml-charmhub)= +## `charmhub` + + +```{note} + +In Charmcraft 3.0 and up, these keys will no longer be valid in `charmcraft.yaml`. Use the environment variables `CHARMCRAFT_STORE_API_URL`, `CHARMCRAFT_UPLOAD_URL` and `CHARMCRAFT_REGISTRY_URL` instead. + +``` + +**Status:** Optional. + +**Purpose:** Configures Charmcraft's interaction with store servers. + +**Structure:** This key allows for the configuration of two values---the base URL for the Charmhub API and the base URL to push binaries to Charmhub. These keys are also optional. + +``` +charmhub: + api-url: + storage-url: + registry-url: +``` + +The key is used mostly in the context of "private" charm stores, defaulting to the standard Canonical services to operate with charms. + +**Example:** + +```text +charmhub: + api-url: https://api.staging.charmhub.io + storage-url: https://storage.staging.snapcraftcontent.com +``` + +(file-charmcraft-yaml-config)= +## `config` + +> See first: [Juju | Application configuration](https://juju.is/docs/juju/configuration) + +**Status:** Optional. + +**Purpose:** The `config` key allows you to create configuration options for your charm. + + +```yaml +config: + options: + # Each option name is the name by which the charm will query the option. +

+ +# (Required) A full description of the configuration layer +description: | + + +# (Optional) A list of maintainers in the format "First Last " +maintainers: + - + +# (Optional) A string (or a list of strings) containing a link (or links) to project websites. +# In general this is likely to be the upstream project website, or the formal website for the +# charmed offering. +website: | [] + +# (Optional) A string (or a list of strings) containing a link (or links) to the charm source code. +source: | [] + +# (Optional) A string (or a list of strings) containing a link (or links) to the charm bug tracker. +issues: | [] + +# (Optional) A link to a documentation cover page on Discourse +# More details at https://juju.is/docs/sdk/add-docs-to-your-charmhub-page +docs: + +# (Optional) A list of terms that any charm user must agree with +terms: + - + +# (Optional) True if the charm is meant to be deployed as a subordinate to a +# principal charm +subordinate: true | false + +# (Optional) A map of containers to be created adjacent to the charm. This field +# is required when the charm is targeting Kubernetes, where each of the specified +# containers will be created as sidecars to the charm in the same pod. +containers: + # Each key represents the name of the container + : + # Note: One of either resource or bases must be specified. + + # (Optional) Reference for an entry in the resources field. Specifies + # the oci-image resource used to create the container. Must not be + # present if a base/channel is specified + resource: + + # (Optional) A list of bases in descending order of preference for use + # in resolving a container image. Must not be present if resource is + # specified. These bases are listed as base (instead of name) and + # channel as in the Base definition, as an unnamed top-level object list + bases: + # Name of the OS. For example ubuntu/centos/windows/osx/opensuse + - name: + + # Channel of the OS in format "track[/risk][/branch]" such as used by + # Snaps. For example 20.04/stable or 18.04/stable/fips + channel: + + # List of architectures that this particular charm can run on + architectures: + - + + # (Optional) List of mounted storages for this container + mounts: + # (Required) Name of the storage to mount from the charm storage + - storage: + + # (Optional) In the case of filesystem storages, the location to + # mount the storage. For multi-stores, the location acts as the + # parent directory for each mounted store. + location: + + # (Optional) UID to run the pebble entry process for this container as. + # Can be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved for users). + # Default is 0 (root). Added in Juju 3.5.0. + uid: + + # (Optional) GID to run the pebble entry process for this container as. + # Can be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved for user's primary groups). + # Default is 0 (root). Added in Juju 3.5.0. + gid: + +# (Optional) Additional resources that accompany the charm +resources: + # Each key represents the name of the resource + : + + # (Required) The type of the resource + type: file | oci-image + + # (Optional) Description of the resource and its purpose + description: + + # (Required: when type:file) The filename of the resource as it should + # appear in the filesystem + filename: + +# (Optional) Map of relations provided by this charm +provides: + # Each key represents the name of the relation as known by this charm + : + + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container + +# (Optional) Map of relations required by this charm +requires: + # Each key represents the name of the relation as known by this charm + : + + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container + +# (Optional) Mutual relations between units/peers of this charm +peers: + # Each key represents the name of the relation as known by this charm + : + + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container + +# (Optional) Storage requests for the charm +storage: + # Each key represents the name of the storage + : + + # (Required) Type of the requested storage + type: filesystem | block + + # (Optional) Description of the storage requested + description: + + # (Optional) The mount location for filesystem stores. For multi-stores + # the location acts as the parent directory for each mounted store. + location: + + # (Optional) Indicates if all units of the application share the storage + shared: true | false + + # (Optional) Indicates if the storage should be made read-only (where possible) + read-only: true | false + + # (Optional) The number of storage instances to be requested + multiple: | - | - | + + + # (Optional) Minimum size of requested storage in forms G, GiB, GB. Size + # multipliers are M, G, T, P, E, Z or Y. With no multiplier supplied, M + # is implied. + minimum-size: | + + # (Optional) List of properties, only supported value is "transient" + properties: + - transient + +# (Optional) Device requests for the charm, for example a GPU +devices: + # Each key represents the name of the device + : + + # (Required) The type of device requested + type: gpu | nvidia.com/gpu | amd.com/gpu + + # (Optional) Description of the requested device + description: + + # (Optional) Minimum number of devices required + countmin: + + # (Optional) Maximum number of devices required + countmax: + +# (Optional) Extra bindings for the charm. For example binding extra network +# interfaces. Key only map, value must be blank. Key represents the name +extra-bindings: + # Key only map; key represents the name of the binding + : + +# (Optional) A set of features that must be provided by the Juju model to ensure that the charm can be successfully deployed. +# See https://juju.is/docs/olm/supported-features for the full list. +assumes: + - + - any-of: + - + - + - all-of: + - + - + +# (Optional) What kind of user is required to run the charm code. +# It can be one of root, sudoer or non-root. +# Added in Juju 3.6.0. If not specified, root is assumed. +charm-user: +``` + + +``` +---------------------------- + + +**Contents:** + +- [`assumes`](#heading--assumes) +- [`charm-user`](#heading--charm-user) +- [`containers`](#heading--containers) +- [`description`](#heading--description) +- [`devices`](#heading--devices) +- [`display-name`](#heading--display-name) +- [`docs`](#heading--docs) +- [`extra-bindings`](#heading--extra-bindings) +- [`issues`](#heading--issues) +- [`maintainers`](#heading--maintainers) +- [`name`](#heading--name) +- [`peer`](#heading--peer) +- [`provides`](#heading--provides) +- [`requires`](#heading--requires) +- [`resources`](#heading--resources) +- [`source`](#heading--source) +- [`storage`](#heading--storage) +- [`subordinate`](#heading--subordinate) +- [`summary`](#heading--summary) +- [`terms`](#heading--terms) +- [`website`](#heading--website) +- [``](#heading--other-arbitrary-keywords) + + +

`assumes`

+ + + +**Status:** Optional. Recommended for Kubernetes charms. + +**Purpose:** The `assumes` key allows charm authors to explicitly state in the metadata of a charm various features that a Juju model must be able to provide to ensure that the charm can be successfully deployed on it. When a charm comes preloaded with such requirements, this enables Juju to perform a pre-deployment check and to display user-friendly error messages if a feature requirement cannot be met by the model that the user is trying to deploy the charm to. If the assumes section of the charm metadata is omitted, Juju will make a best-effort attempt to deploy the charm, and users must rely on the output of `juju status` to figure out whether the deployment was successful. The `assumes` key is available since 2.9.23. + +**Structure:** The key consists of a list of features that can be given either directly or, depending on the complexity of the condition you want to enforce, nested under one or both of the boolean expressions `any-of` or `all-of`, as shown below. In order for a charm to be deployed, all entries in the `assumes` block must be satisfied. + +```yaml +assumes: + - + - any-of: + - + - + - all-of: + - + - +``` + + The supported feature names are as below: + +|||| +|-|-|-| +|`juju `

E.g., `juju < 3.0`.
E.g., juju >= 2.9` | The charm deploys if and only if the model runs agent binaries with the specified Juju version(s). |Since 2.9.23| +|`k8s-api` | The charm deploys if and only if the underlying substrate for the model is Kubernetes. |Since 2.9.23| + +The Boolean expressions are defined as below: + +||| +|-|-| +|`any-of`| The sub-expression is satisfied if any of the provided child expressions is satisfied.| +|`all-of` | The sub-expression is satisfied if all of the provided child expressions are satisfied.| + +**Examples:** + +---- +```{dropdown} Expand to see a simple example + + +```yaml +assumes: + - juju >= 2.9.23 + - k8s-api +``` + +``` + +------------- + +```{dropdown} Expand to see an example with a nested expression + +```yaml +assumes: + - any_of: + - juju >= 2.9 + - k8s-api +``` + +``` + +------------- + +

`charm-user`

+ +```{important} + +`charm-user` was added in Juju 3.6.0. Currently is only supported on Kubernetes charms and has no effect on machine charms. + +``` + +**Status:** Optional. Recommended for Kubernetes charms. + +**Purpose:** The `charm-user` key allows charm authors to specify that their charm hook code does not need to be run as root. This key, in addition to `uid` + `uid` fields in `containers`, allows charms to be run rootless. The value of `root` ensures the charm runs as root. Both `sudoer` and `non-root` will run as a user other than root. In the case of the value `sudoer`, the charm will be run as a user with access to sudo to elevate it's privileges. + +**Structure:** The key consists of a single value. One of `root`, `sudoer` or `non-root`. + +```yaml +# (Optional) What kind of user is required to run the charm code. +# It can be one of root, sudoer or non-root. +# Added in Juju 3.6.0. If not specified, root is assumed. +charm-user: +``` + +--------- + + +

`containers`

+> See also: [`resources`](#heading--resources) + +**Status:** Required for Kubernetes charms (except for proxy charms running on Kubernetes). + +**Purpose:** The `containers` key allows you to define a map of containers to be created adjacent to the charm (as a sidecar, in the same pod). + +**Structure:** This key consists of a list of containers along with their specification. Each container can be specified in terms of `resource`, `bases`, `uid`, `gid` and `mounts`, where one of either the `resource` or the `bases` subkeys must be defined, and `mounts` is optional. `resource` stands for the OCI image resource used to create the container; to use it, specify an OCI image resource name (that you will then define further in the [`resources`](#heading--resources) block). `bases` is a list of bases to be used for resolving a container image, in descending order of preference; to use it, specify a base name (for example, `ubuntu`, `centos`, `windows`, `osx`, `opensuse`), a [channel](https://snapcraft.io/docs/channels), and an architecture. `mounts` is a list of mounted storages for this container; to use it, specify the name of the storage to mount from the charm storage and, optionally, the location where to mount the storage. And, starting with Juju 3.5.0, `uid` and `gid` are the UID and, respectively, GID to run the Pebble entry process for this container as; they can be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved for users) and the default is 0 (root). + +```yaml +containers: + : + resource: + bases: + - name: + channel: + architectures: + - + mounts: + - storage: + location: + uid: + gid: +``` + + + +**Examples:** + +```{dropdown} Expand to see an example with `resource` and `mounts` + +```yaml +containers: + super-app: + resource: super-app-image + mounts: + - storage: logs + location: /logs +``` + +``` + + +

`description`

+ +**Status:** Required. + +**Purpose:** The `description` key is where you provide a full description of the configuration layer. + +**Structure:** + +```yaml +description: | + +``` + + + +

`devices`

+ +```yaml + +# (Optional) Device requests for the charm, for example a GPU +devices: + # Each key represents the name of the device + : + + # (Required) The type of device requested + type: gpu | nvidia.com/gpu | amd.com/gpu + + # (Optional) Description of the requested device + description: + + # (Optional) Minimum number of devices required + countmin: + + # (Optional) Maximum number of devices required + countmax: + +``` + +

`display-name`

+ +```{important} + +In [`charmcraft.yaml` ` this is now the `title` key. + +``` + +```yaml +display-name: | + +``` + +

`docs`

+> See also: {ref}`How to create an effective README file for your charm ` + +```{important} + +In {ref}``charmcraft.yaml` ` this is now the `documentation` subkey under the `links` key. + +``` + + +```yaml +# (Optional) A link to a documentation cover page on Discourse: +docs: +``` + +

`extra-bindings`

+ +```yaml +# (Optional) Extra bindings for the charm. For example binding extra network +# interfaces. Key only map, value must be blank. Key represents the name. +extra-bindings: + # Key only map; key represents the name of the binding + : +``` + +

`issues`

+ +```{important} + +In {ref}``charmcraft.yaml` ` this is now under the `links` key. + +``` + + +```yaml +# (Optional) A string (or a list of strings) containing a link (or links) to the charm bug tracker. +issues: | {ref}`] +``` +

`maintainers`

+ +```{important} + +In [`charmcraft.yaml` ` this is now the `contact` subkey under the `links` key. + +``` + + +```yaml +# (Optional) A list of maintainers in the format "First Last " +maintainers: + - +``` + +

`name`

+ +```yaml +# (Required) The name of the charm. Determines URL in Charmhub and the name administrators +# will ultimately use to deploy the charm. E.g. `juju deploy ` +name: +``` + +

`peer`

+ +```yaml +# (Optional) Mutual relations between units/peers of this charm +peer: + # Each key represents the name of the relation as known by this charm + : + + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container +``` + +

`provides`

+ +```yaml +# (Optional) Map of relations provided by this charm +provides: + # Each key represents the name of the relation as known by this charm + : + + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container +``` + +

`requires`

+ +```yaml +# (Optional) Map of relations required by this charm +requires: + # Each key represents the name of the relation as known by this charm + : + + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container + +``` + +

`resources`

+> See also: {ref}`Resource <5213md>` + + +**Purpose:** The `resources` key is where you define the resources mentioned under the `resource` key of the [`containers`](#heading--containers) key. + +**Structure:** + + + +```yaml +# (Optional) Additional resources that accompany the charm +resources: + # Each key represents the name of a resource + # mentioned in the 'resource' subkey of the 'containers' key. + : + + # (Required) The type of the resource + type: file | oci-image + + # (Optional) Description of the resource and its purpose + description: + + # (Required: when type:file) The filename of the resource as it should + # appear in the filesystem + filename: +``` + +**Examples:** + +------ +```{dropdown} Expand to see an example with an OCI-image resource + +```yaml +resources: + super-app-image: + type: oci-image + description: OCI image for the Super App (hub.docker.com/_/super-app) +``` + +``` +---- + +

`source`

+ +```{important} + +In {ref}``charmcraft.yaml` ` this is now under the `links` key. + +``` + + +```yaml +# (Optional) A string (or a list of strings) containing a link (or links) to the charm source code. +source: | {ref}`] +``` + +

`storage`

+ +```yaml +# (Optional) Storage requests for the charm +storage: + # Each key represents the name of the storage + : + + # (Required) Type of the requested storage + type: filesystem | block + + # (Optional) Description of the storage requested + description: + + # (Optional) The mount location for filesystem stores. For multi-stores + # the location acts as the parent directory for each mounted store. + location: + + # (Optional) Indicates if all units of the application share the storage + shared: true | false + + # (Optional) Indicates if the storage should be made read-only (where possible) + read-only: true | false + + # (Optional) The number of storage instances to be requested + multiple: + range: | - | - | + + + # (Optional) Minimum size of requested storage in forms G, GiB, GB. Size + # multipliers are M, G, T, P, E, Z or Y. With no multiplier supplied, M + # is implied. + minimum-size: | + + # (Optional) List of properties, only supported value is "transient" + properties: + - transient +``` + + +

`subordinate`

+ +```yaml +# (Optional) True if the charm is meant to be deployed as a subordinate to a +# principal charm +subordinate: true | false +``` + +

`summary`

+ +```yaml +# (Required) A short, one-line description of the charm +summary: +``` + +

`terms`

+ +```yaml +# (Optional) A list of terms that any charm user must agree with +terms: + - +``` + +

`website`

+ +```{important} + +In [`charmcraft.yaml` ` this is now under the `links` key. + +``` + + +```yaml +# (Optional) A string (or a list of strings) containing a link (or links) to project websites. +# In general this is likely to be the upstream project website, or the formal website for the +# charmed offering. +website: | [] +``` + +

``

+ +In addition to the official fields and keywords mentioned above, a `metadata.yaml` file may also contain other arbitrary keywords. These can serve to keep track of other choices a charmer might make. In some cases these become semi-official, being adopted by many charmers and even incorporated into CI processes. An example is [`upstream-source`](https://github.com/canonical/charmcraft/blob/b22fcdba3b894004468abfbf45caa54d93fbf7d0/charmcraft/templates/init-simple/metadata.yaml.j2#L40-L43). diff --git a/docs/reference/files/file-pyproject-toml.md b/docs/reference/files/file-pyproject-toml.md new file mode 100644 index 000000000..e6327840e --- /dev/null +++ b/docs/reference/files/file-pyproject-toml.md @@ -0,0 +1,56 @@ +(file-pyproject-toml)= +# File 'pyproject.toml' + +The `pyproject.toml` file in your charm's root directory is a typical Python `pyproject.toml` file. + +> See more: [`pip` | `pyproject.toml`](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) + +This file is generated automatically by `charmcraft init` with the contents below: + +```text +# Testing tools configuration +[tool.coverage.run] +branch = true + +[tool.coverage.report] +show_missing = true + +[tool.pytest.ini_options] +minversion = "6.0" +log_cli_level = "INFO" + +# Formatting tools configuration +[tool.black] +line-length = 99 +target-version = ["py38"] + +# Linting tools configuration +[tool.ruff] +line-length = 99 +select = ["E", "W", "F", "C", "N", "D", "I001"] +extend-ignore = [ + "D203", + "D204", + "D213", + "D215", + "D400", + "D404", + "D406", + "D407", + "D408", + "D409", + "D413", +] +ignore = ["E501", "D107"] +extend-exclude = ["__pycache__", "*.egg_info"] +per-file-ignores = {"tests/*" = ["D100","D101","D102","D103","D104"]} + +[tool.ruff.mccabe] +max-complexity = 10 + +[tool.codespell] +skip = "build,lib,venv,icon.svg,.tox,.git,.mypy_cache,.ruff_cache,.coverage" + +[tool.pyright] +include = ["src/**.py"] +``` diff --git a/docs/reference/files/file-readme-md.md b/docs/reference/files/file-readme-md.md new file mode 100644 index 000000000..14f0159c4 --- /dev/null +++ b/docs/reference/files/file-readme-md.md @@ -0,0 +1,7 @@ +(file-readme-md)= +# File 'README.md' + + > {ref}`List of files in the charm project ` > `README.md` + + +The `README.md` file should describe the charm’s behaviour and provide instructions on to deploy the charm as well as links to resources for the supported application. diff --git a/docs/reference/files/file-requirements-dev-txt.md b/docs/reference/files/file-requirements-dev-txt.md new file mode 100644 index 000000000..466594651 --- /dev/null +++ b/docs/reference/files/file-requirements-dev-txt.md @@ -0,0 +1,6 @@ +(file-requirements-dev-txt)= +# File 'requirements-dev.txt' + +> {ref}`List of files in the charm project ` > `requirements-dev.txt` + +The `requirements-dev.txt` is a standard Python [requirements file](https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format) that specifies only those dependencies that are used during development. Examples of this might include testing libraries, linters, etc. These dependencies will not be bundled with the charm when it is built. diff --git a/docs/reference/files/file-requirements-txt.md b/docs/reference/files/file-requirements-txt.md new file mode 100644 index 000000000..e1ab41fdb --- /dev/null +++ b/docs/reference/files/file-requirements-txt.md @@ -0,0 +1,8 @@ +(file-requirements-txt)= +# File 'requirements.txt' + +> {ref}`List of files in the charm project ` > `requirements.txt` + + + +The `requirements.txt` file is a standard Python [requirements file](https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format) used to declare and pin the version of any Python libraries required by a charm in production. This will be pre-populated with {ref}``ops` `. Any dependencies specified here will be bundled with the charm when it is built with {ref}``charmcraft pack` `. diff --git a/docs/reference/files/file-src-charm-py.md b/docs/reference/files/file-src-charm-py.md new file mode 100644 index 000000000..c66629bfd --- /dev/null +++ b/docs/reference/files/file-src-charm-py.md @@ -0,0 +1,7 @@ +(file-src-charm-py)= +# File 'src/charm.py' + +> {ref}`List of files in the charm project ` > `src/charm.py` + + +The `src/charm.py` is the default entry point for a charm. This file must be executable, and should include a {ref}`shebang <7150md>`>) to indicate the desired interpreter. For many charms, this file will contain the majority of the charm code. It is possible to change the name of this file, but additional changes are then required to enable the charm to be built with {ref}``charmcraft` `. diff --git a/docs/reference/files/file-tests-integration-test-charm-py.md b/docs/reference/files/file-tests-integration-test-charm-py.md new file mode 100644 index 000000000..43b3065e2 --- /dev/null +++ b/docs/reference/files/file-tests-integration-test-charm-py.md @@ -0,0 +1,48 @@ +(file-tests-integration-test-charm-py)= +# File ‘tests/integration/test_charm.py’ + +The `tests/integration/test_charm.py` file is the companion to `src/charm.py` for integration testing. + +This file is created automatically by `charmcraft init` and it is pre-populated with standard constructs used by `pytest-operator`, similar to the below: + +```text +#!/usr/bin/env python3 +# Copyright 2023 Ubuntu +# See LICENSE file for licensing details. + +import asyncio +import logging +from pathlib import Path + +import pytest +import yaml +from pytest_operator.plugin import OpsTest + +logger = logging.getLogger(__name__) + +METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) +APP_NAME = METADATA["name"] + + +@pytest.mark.abort_on_fail +async def test_build_and_deploy(ops_test: OpsTest): + """Build the charm-under-test and deploy it together with related charms. + + Assert on the unit status before any relations/configurations take place. + """ + # Build and deploy charm from local source folder + charm = await ops_test.build_charm(".") + resources = { + "some-container-image": METADATA["resources"]["some-container-image"]["upstream-source"] + } + + # Deploy the charm and wait for active/idle status + await asyncio.gather( + ops_test.model.deploy(charm, resources=resources, application_name=APP_NAME), + ops_test.model.wait_for_idle( + apps=[APP_NAME], status="active", raise_on_blocked=True, timeout=1000 + ), + ) + + +``` diff --git a/docs/reference/files/file-tests-unit-test-charm-py.md b/docs/reference/files/file-tests-unit-test-charm-py.md new file mode 100644 index 000000000..afce39153 --- /dev/null +++ b/docs/reference/files/file-tests-unit-test-charm-py.md @@ -0,0 +1,37 @@ +(file-tests-unit-test-charm-py)= +# File 'tests/unit/test_charm.py' + +> {ref}`List of files in the charm project ` > `tests/test_charm.py` +> +> See also: {ref}`How to test a charm ` + +The `tests/unit/test_charm.py` file is the companion to `src/charm.py` for unit testing. It is pre-populated with standard constructs used by `unittest` and Harness. + +This file is created automatically by `charmcraft init` and it is pre-populated with standard constructs used by `unittest` and `Harness`, along the lines below: + +```text + +# Copyright 2023 Ubuntu +# See LICENSE file for licensing details. +# +# Learn more about testing at: https://juju.is/docs/sdk/testing + +import unittest + +import ops +import ops.testing +from charm import MyK8SCharmCharm + + +class TestCharm(unittest.TestCase): + def setUp(self): + self.harness = ops.testing.Harness(MyK8SCharmCharm) + self.addCleanup(self.harness.cleanup) + self.harness.begin() + + def test_pebble_ready(self): + # Simulate the container coming up and emission of pebble-ready event + self.harness.container_pebble_ready("some-container") + # Ensure we set an ActiveStatus with no message + self.assertEqual(self.harness.model.unit.status, ops.ActiveStatus()) +``` diff --git a/docs/reference/files/file-tox-ini.md b/docs/reference/files/file-tox-ini.md new file mode 100644 index 000000000..fb740a92d --- /dev/null +++ b/docs/reference/files/file-tox-ini.md @@ -0,0 +1,97 @@ +(file-tox-ini)= +# File 'tox.ini' + +The `tox.ini` file in your charm’s root directory is a typical Tox configuration file. + +> See more: [Tox | Configuration](https://tox.wiki/en/latest/user_guide.html#configuration) + +This file is generated automatically by `charmcraft init` with the contents below: + +```text + Copyright 2023 Ubuntu +# See LICENSE file for licensing details. + +[tox] +no_package = True +skip_missing_interpreters = True +env_list = format, lint, static, unit +min_version = 4.0.0 + +[vars] +src_path = {tox_root}/src +tests_path = {tox_root}/tests +;lib_path = {tox_root}/lib/charms/operator_name_with_underscores +all_path = {[vars]src_path} {[vars]tests_path} + +[testenv] +set_env = + PYTHONPATH = {tox_root}/lib:{[vars]src_path} + PYTHONBREAKPOINT=pdb.set_trace + PY_COLORS=1 +pass_env = + PYTHONPATH + CHARM_BUILD_DIR + MODEL_SETTINGS + +[testenv:format] +description = Apply coding style standards to code +deps = + black + ruff +commands = + black {[vars]all_path} + ruff --fix {[vars]all_path} + +[testenv:lint] +description = Check code against coding style standards +deps = + black + ruff + codespell +commands = + # if this charm owns a lib, uncomment "lib_path" variable + # and uncomment the following line + # codespell {[vars]lib_path} + codespell {tox_root} + ruff {[vars]all_path} + black --check --diff {[vars]all_path} + +[testenv:unit] +description = Run unit tests +deps = + pytest + coverage[toml] + -r {tox_root}/requirements.txt +commands = + coverage run --source={[vars]src_path} \ + -m pytest \ + --tb native \ + -v \ + -s \ + {posargs} \ + {[vars]tests_path}/unit + coverage report + +[testenv:static] +description = Run static type checks +deps = + pyright + -r {tox_root}/requirements.txt +commands = + pyright {posargs} + +[testenv:integration] +description = Run integration tests +deps = + pytest + juju + pytest-operator + -r {tox_root}/requirements.txt +commands = + pytest -v \ + -s \ + --tb native \ + --log-cli-level=INFO \ + {posargs} \ + {[vars]tests_path}/integration +``` diff --git a/docs/reference/files/index.md b/docs/reference/files/index.md new file mode 100644 index 000000000..6cd6bb14e --- /dev/null +++ b/docs/reference/files/index.md @@ -0,0 +1,30 @@ +(list-of-files-in-a-charm-or-a-bundle)= +# List of files in a charm or a bundle + + +```{toctree} +:maxdepth: 1 + + +file-bundle-yaml +file-actions-yaml +file-charmcraft-yaml +file-config-yaml +file-contributing-md +file-dispatch +file-icon-svg +file-libname-py +file-license +file-lxd-profile-yaml +file-manifest-yaml +file-metadata-yaml +file-pyproject-toml +file-readme-md +file-requirements-dev-txt +file-requirements-txt +file-src-charm-py +file-tests-unit-test-charm-py +file-tests-integration-test-charm-py +file-tox-ini +``` + diff --git a/docs/reference/index.rst b/docs/reference/index.rst index ccb31aa63..f8e277e7d 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -3,15 +3,14 @@ Reference ********* -Charmcraft is a part of the `Juju Charm SDK `_. This -documentation only contains references about Charmcraft itself. Overall SDK reference -data can be found in the `Charm SDK docs `_. - .. toctree:: :maxdepth: 2 - commands - models/index - parts - plugins/index - changelog + charmcraft + charmcraft-analyzers-and-linters + charmcraft-cli + extension + file + part + profile + diff --git a/docs/explanation/lifecycle.rst b/docs/reference/lifecycle.rst similarity index 99% rename from docs/explanation/lifecycle.rst rename to docs/reference/lifecycle.rst index eb45430ef..9bd1a44fb 100644 --- a/docs/explanation/lifecycle.rst +++ b/docs/reference/lifecycle.rst @@ -43,3 +43,4 @@ Further Information ------------------- Further information can be found in the `Craft-parts`_ documentation. + diff --git a/docs/reference/commands.rst b/docs/reference/list-of-charmcraft-cli-commands.rst similarity index 92% rename from docs/reference/commands.rst rename to docs/reference/list-of-charmcraft-cli-commands.rst index e03b4e629..79d85ef4c 100644 --- a/docs/reference/commands.rst +++ b/docs/reference/list-of-charmcraft-cli-commands.rst @@ -1,7 +1,7 @@ .. _commands: -Commands -******** +List of ``charmcraft`` CLI commands +********************************* .. Use a hidden table of contents to ensure that documentation is read. @@ -48,3 +48,4 @@ Other Commands ============== .. include:: commands/other-commands.rst + diff --git a/docs/reference/parts.rst b/docs/reference/part.rst similarity index 75% rename from docs/reference/parts.rst rename to docs/reference/part.rst index 4ea8ae85e..c08930cdc 100644 --- a/docs/reference/parts.rst +++ b/docs/reference/part.rst @@ -1,7 +1,9 @@ -.. _parts: +.. _part: + +**** +Part +**** -Parts -***** Parts, powered by :external+craft-parts:ref:`craft-parts `, power the build system that charmcraft uses. @@ -12,3 +14,7 @@ system that charmcraft uses. /common/craft-parts/reference/part_properties /common/craft-parts/reference/parts_steps /common/craft-parts/reference/step_execution_environment + /common/craft-parts/explanation/filesets + lifecycle + plugins/index + diff --git a/docs/reference/plugins/index.rst b/docs/reference/plugins/index.rst index dce9a13a3..60b739c5f 100644 --- a/docs/reference/plugins/index.rst +++ b/docs/reference/plugins/index.rst @@ -1,7 +1,7 @@ .. _plugins: -Parts plugins -************* +List of part plugins +******************** Most charms only need one, maybe two parts, typically consisting of one of Charmcraft's application-specific plugins such as the `charm plugin`_ or the `reactive plugin`_ and @@ -11,6 +11,7 @@ potentially the addition of further files using the :ref:`craft_parts_dump_plugi :maxdepth: 1 /common/craft-parts/reference/plugins/dump_plugin + /common/craft-parts/explanation/dump_plugin /common/craft-parts/reference/plugins/nil_plugin python_plugin poetry_plugin diff --git a/docs/reference/profile.md b/docs/reference/profile.md new file mode 100644 index 000000000..1bf6b8e2c --- /dev/null +++ b/docs/reference/profile.md @@ -0,0 +1,8 @@ +(profile)= +# Profile + +In the context of building a charm, in Rockcraft and Charmcraft, a **profile** is a name you can pass as an argument during rock / charm initialisation that will create all the usual rock/charm project files in a form customised for a specific purpose -- i.e., for a Kubernetes charm, for a Kubernetes charm for a Flask application etc. -- in order to speed up development. + +The customisation often takes the shape of a specific {ref}`extension ` in the charm's `charmcraft.yaml` file. + +> See more: {ref}`How to set up a charm project ` \ No newline at end of file diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md new file mode 100644 index 000000000..2d8da9968 --- /dev/null +++ b/docs/tutorial/index.md @@ -0,0 +1,19 @@ +(tutorial)= +# Tutorial + +Our tutorial comes in multiple flavours -- pick your flavour of choice! + +```{note} +Currently all our tutorial flavours demonstrate `charmcraft` in combination with an 12-factor app extension -- a journey that does not require any [`ops`](https://ops.readthedocs.io/en/latest/#) and which does not currently include publishing the charm. To get a sense of the workflow for other types of applications and including the publication step, see {ref}`manage-charms`. +``` + + +```{toctree} +:maxdepth: 2 + + +Write your first Kubernetes charm for a Django app +Write your first Kubernetes charm for a FastAPI app +Write your first Kubernetes charm for a Go app + +``` diff --git a/docs/tutorial/write-your-first-kubernetes-charm-for-a-django-app.md b/docs/tutorial/write-your-first-kubernetes-charm-for-a-django-app.md new file mode 100644 index 000000000..2ec4bce77 --- /dev/null +++ b/docs/tutorial/write-your-first-kubernetes-charm-for-a-django-app.md @@ -0,0 +1,567 @@ +(write-your-first-kubernetes-charm-for-a-django-app)= +# Write your first Kubernetes charm for a Django app + + +**What you’ll need:** + +- A working station, e.g., a laptop, with amd64 architecture which has sufficient resources to launch a virtual machine with 4 CPUs, 4 GB RAM, and a 50 GB disk + - Note that a workstation with arm64 architecture can complete the majority of this tutorial. +- Familiarity with Linux. +- About an hour of free time. + +**What you’ll do:** + +Create a Django application. Use that to create a rock with `rockcraft`. Use that to create a charm with `charmcraft`. Use that to test-deploy, configure, etc., your Django application on a local Kubernetes cloud, `microk8s`, with `juju`. All of that multiple times, mimicking a real development process. + +```{note} + +**rock** + +An Ubuntu LTS-based OCI compatible container image designed to meet security, stability, and reliability requirements for cloud-native software. + +**charm** + +A package consisting of YAML files + Python code that will automate every aspect of an application's lifecycle so it can be easily orchestrated with Juju. + +**`juju`** + +An orchestration engine for charmed applications. + +``` + + +```{important} + +**Should you get stuck or notice issues:** Please get in touch on [Matrix](https://matrix.to/#/#12-factor-charms:ubuntu.com) or [Discourse](https://discourse.charmhub.io/). + +``` + +## Set things up + +Install Multipass. + +> 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 22.04 blueprint: + +```bash +multipass launch --cpus 4 --disk 50G --memory 4G --name charm-dev 22.04 +``` + +Once the VM is up, open a shell into it: + +```bash +multipass shell charm-dev +``` + +In order to create the rock, you'll need to install Rockcraft: + +```bash +sudo snap install rockcraft --classic +``` + +`LXD` will be required for building the rock. Make sure it is installed and initialised: + +```bash +sudo snap install lxd +lxd init --auto +``` + +In order to create the charm, you'll need to install Charmcraft: + +```bash +sudo snap install charmcraft --channel latest/edge --classic +``` + +```{note} + +This tutorial requires version `3.2.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 Django application on Kubernetes. Install MicroK8s: + +```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: + +```bash +sudo microk8s enable hostpath-storage +# Required to host the OCI image of the Django application +sudo microk8s enable registry +# Required to expose the Django application +sudo microk8s enable ingress +``` + +Juju is required to deploy the Django application. Install Juju and bootstrap a development controller: + +```bash +sudo snap install juju --channel 3.5/stable +mkdir -p ~/.local/share +juju bootstrap microk8s dev-controller +``` + +Finally, create a new directory for this tutorial and go inside it: + +```bash +mkdir django-hello-world +cd django-hello-world +``` + +## Create the Django application + + +Create a `requirements.txt` file, copy the following text into it and then save it: + +``` +Django +``` + +Install `python3-venv` and create a virtual environment: + +```bash +sudo apt-get update && sudo apt-get install python3-venv -y +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +Create a new project using `django-admin`: + +```bash +django-admin startproject django_hello_world +``` + +## Run the Django application locally + +Change into the `django_hello_world` directory and run the Django application to verify that it works: + +```bash +cd django_hello_world +python3 manage.py runserver +``` + +Test the Django application by using `curl` to send a request to the root endpoint. You may need a new terminal for this; if you are using Multipass use `multipass shell charm-dev` to get another terminal: + +```bash +curl localhost:8000 +``` + +The Django application should respond with `The install worked successfully! Congratulations!`. + +```{note} + +The response from the Django application includes HTML and CSS which makes it difficult to read on a terminal. + +``` + +The Django application looks good, so you can stop it for now using `ctrl+C`. + +## Pack the Django application into a rock + +First, we'll need a `rockcraft.yaml` file. Rockcraft will automate its creation and tailoring for a Django application by using the `django-framework` profile: + +```bash +cd .. +rockcraft init --profile django-framework +``` + +The `rockcraft.yaml` file will automatically be created and set the name based on your working directory. Open it in a text editor and check that the `name` is `django-hello-world`. 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 `django-hello-world` and assume you are on the `amd64` platform. Check the architecture of your system using `dpkg --print-architecture`. Choosing a different name or running on a different platform will influence the names of the files generated by Rockcraft. + +``` + +Django applications require a database. Django will use a sqlite database by default. This won't work on Kubernetes because the database would disappear every time the pod is restarted (e.g., to perform an upgrade) and this database would not be shared by all containers as the application is scaled. We'll use Juju later to easily deploy a database. + +We'll need to update the `settings.py` file to prepare for integrating the app with a database. Open `django_hello_world/django_hello_world/settings.py` and include `import json`, `import os` and `import secrets` along with the other imports at the top of the file. + +Near the top of the `settings.py` file change the following settings to be production ready: + +```python +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', secrets.token_hex(32)) + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = os.environ.get('DJANGO_DEBUG', 'false') == 'true' + +ALLOWED_HOSTS = json.loads(os.environ.get('DJANGO_ALLOWED_HOSTS', '{ref}`]')) +``` + +Go further down to the Database section and change the `DATABASES` variable to: + +```python +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.environ.get('POSTGRESQL_DB_NAME'), + 'USER': os.environ.get('POSTGRESQL_DB_USERNAME'), + 'PASSWORD': os.environ.get('POSTGRESQL_DB_PASSWORD'), + 'HOST': os.environ.get('POSTGRESQL_DB_HOSTNAME'), + 'PORT': os.environ.get('POSTGRESQL_DB_PORT'), + } +} +``` + +We'll need to update the `requirements.txt` file to include `psycopg2-binary` so that the Django app can connect to PostgreSQL. + +Pack the rock: + +```bash +ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack +``` + +```{note} + +Depending on your network, this step can take a couple of minutes to finish. + +`ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS` is required whilst the Django extension is experimental. + +``` + +Once Rockcraft has finished packing the Django rock, you'll find a new file in your working directory with the `.rock` extension: + +```bash +ls *.rock -l +``` + +The rock needs to be copied to the MicroK8s registry so that it can be deployed in the Kubernetes cluster: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:django-hello-world_0.1_amd64.rock \ + docker://localhost:32000/django-hello-world:0.1 +``` + +```{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. + +``` + +## Create the charm + +Create a new directory for the charm and go inside it: + +```bash +mkdir charm +cd charm +``` + +We'll need a `charmcraft.yaml`, `requirements.txt` and source code for the charm. The source code contains the logic required to operate the Django application. Charmcraft will automate the creation of these files by using the `django-framework` profile: + +```bash +charmcraft init --profile django-framework --name django-hello-world +``` + +The files will automatically be created in your working directory. We will need to connect to the PostgreSQL database. Open the `charmcraft.yaml` file and add the following section to the end of the file: + +```yaml +requires: + postgresql: + interface: postgresql_client + optional: false + limit: 1 +``` + +The charm depends on several libraries. Download the libraries and pack the charm: + +```bash +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft fetch-libs +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack +``` + +```{note} + +Depending on your network, this step can take a couple of minutes to finish. + +``` + +Once Charmcraft has finished packing the charm, you'll find a new file in your working directory with the `.charm` extension: + +```bash +ls *.charm -l +``` + +```{note} + +If you changed the name in charmcraft.yaml or are not on the amd64 platform, the name of the `.charm` file will be different for you. + +``` + +## Deploy the Django application + +A Juju model is needed to deploy the application. Create a new model: + +```bash +juju add-model django-hello-world +``` + +```{note} + +If you are not on a host with the `amd64` architecture, you will need to include a constraint to the Juju model to specify your architecture.For example, for the `arm64` architecture, use `juju set-model-constraints -m django-hello-world arch=arm64`. Check the architecture of your system using `dpkg --print-architecture`. + +``` + +Now deploy the Django application using Juju: + +```bash +juju deploy ./django-hello-world_ubuntu-22.04-amd64.charm \ + django-hello-world \ + --resource django-app-image=localhost:32000/django-hello-world:0.1 +``` + +Deploy PostgreSQL, and integrate and PostgreSQL with the Django application: +```bash +juju deploy postgresql-k8s --trust +juju integrate django-hello-world postgresql-k8s +``` + +```{note} + +It will take a few minutes to deploy the Django application. You can monitor the progress using `juju status --watch 5s`. Once the status of the App has gone to `active`, you can stop watching using `Ctrl+C`. + +``` + +The Django application should now be running. You can see the status of the deployment using `juju status` which should be similar to the following output: + +``` +django-hello-world dev-controller microk8s/localhost 3.5.3 unsupported 16:47:01+10:00 + +App Version Status Scale Charm Channel Rev Address Exposed Message +django-hello-world active 1 django-hello-world 3 10.152.183.126 no +postgresql-k8s 14.11 active 1 postgresql-k8s 14/stable 281 10.152.183.197 no + +Unit Workload Agent Address Ports Message +django-hello-world/0* active idle 10.1.157.80 +postgresql-k8s/0* active idle 10.1.157.78 Primary +``` + +To be able to test the deployment, we need to include the IP address in the allowed hosts configuration. We'll also enable debug mode for now whilst we are testing. Both can be done using `juju config django-hello-world django-allowed-hosts=* django-debug=true`. + +```{note} + +Setting the Django allowed hosts to `*` and turning on debug mode should not be done in production where you should set the actual hostname of the application and disable debug mode. We will do this in the tutorial for now and later demonstrate how we can set these to production ready values. + +``` + +Test the deployment using `curl` to send a request to the root endpoint. The IP address is the Address listed in the Unit section of the `juju status` output (e.g., `10.1.157.80` in the sample output above): + +```bash +curl 10.1.157.80:8000 +``` + +The Django app should again respond with `The install worked successfully! Congratulations!`. + +## Add a root endpoint + +The generated Django application does not come with a root endpoint, which is why we had to initially enable debug mode for testing. Let's add a root endpoint that returns a `Hello, world!` greeting. We will need to go back out to the root directory for the tutorial and go into the `django_hello_world` directory using `cd ../django_hello_world`. Add a new Django app using: + +```bash +django-admin startapp greeting +``` + +Open the `greeting/views.py` file and replace the content with: + +```python +from django.http import HttpResponse + + +def index(request): + return HttpResponse("Hello, world!\n") +``` + +Create the `greeting/urls.py` file with the following contents: + +```python +from django.urls import path + +from . import views + +urlpatterns = [ + path("", views.index, name="index"), +] +``` + +Open the `django_hello_world/urls.py` file and edit the value of `urlpatterns` to include `path('', include("greeting.urls")`, for example: + +```python +from django.contrib import admin +from django.urls import include, path + +urlpatterns = [ + path("", include("greeting.urls")), + path("admin/", admin.site.urls), +] +``` + +Since we're changing the application we should update the version of it. Go back to the root directory of the tutorial using `cd ..` and change the `version` in `rockcraft.yaml` to `0.2`. Pack and upload the rock using similar commands as before: + +```bash +ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:django-hello-world_0.2_amd64.rock \ + docker://localhost:32000/django-hello-world:0.2 +``` + +Now we can deploy the new version of the Django application using: + +```bash +cd charm +juju refresh django-hello-world \ + --path=./django-hello-world_ubuntu-22.04-amd64.charm \ + --resource django-app-image=localhost:32000/django-hello-world:0.2 +``` + +Now that we have a valid root endpoint we can disable debug mode: + +```bash +juju config django-hello-world django-debug=false +``` + +Use `juju status --watch 5s` again to wait until the app is active again. The IP address will have changed so we need to retrieve it again using `juju status`. Now we can call the root endpoint using `curl 10.1.157.80:8000` and the Django application should respond with `Hello, world!`. + +## Enable a configuration + +To demonstrate how to provide configuration to the Django application, we will make the greeting configurable. Go back out to the tutorial root directory using `cd ..`. Open the `django_hello_world/greeting/views.py` file and replace the content with: + +```python +import os + +from django.http import HttpResponse + + +def index(request): + return HttpResponse(f"{os.environ.get('DJANGO_GREETING', 'Hello, world!')}\n") +``` + +Increment the `version` in `rockcraft.yaml` to `0.3` and run the pack and upload commands for the rock: + +```bash +ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:django-hello-world_0.3_amd64.rock \ + docker://localhost:32000/django-hello-world:0.3 +``` + +Change back into the charm directory using `cd charm`. The `django-framework` Charmcraft extension supports adding configurations in `charmcraft.yaml` which will be passed as environment variables to the Django application. Add the following to the end of the `charmcraft.yaml` file: + +```yaml +config: + options: + greeting: + description: | + The greeting to be returned by the Django application. + default: "Hello, world!" + type: string +``` + +```{note} + +Configuration options are automatically capitalised and `-` are replaced by `_`. A `DJANGO_` prefix will also be added as a namespace for app configurations. + +``` + +We can now pack and deploy the new version of the Django app: + +```bash +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack +juju refresh django-hello-world \ + --path=./django-hello-world_ubuntu-22.04-amd64.charm \ + --resource django-app-image=localhost:32000/django-hello-world:0.3 +``` + +After we wait for a bit monitoring `juju status` the application should go back to `active` again. Sending a request to the root endpoint using `curl 10.1.157.81:8000` (after getting the IP address from `juju status`) should result in the Django application responding with `Hello, world!` again. We can change the greeting using `juju config django-hello-world greeting='Hi!'`. After we wait for a moment for the app to be restarted, `curl 10.1.157.81:8000` should now respond with `Hi!`. + +## Expose the app using ingress + +```{note} + +This step of the tutorial only works for hosts with the `amd64` architecture. For other architectures, skip this step. + +``` + +As a final step, let's expose the application using ingress. Deploy the `nginx-ingress-integrator` charm and integrate it with the Django app: + +```bash +juju deploy nginx-ingress-integrator +juju integrate nginx-ingress-integrator django-hello-world +``` + +```{note} + +RBAC is enabled in the `charm-dev` Multipass blueprint. Run `juju trust nginx-ingress-integrator --scope cluster` if you're using the `charm-dev` blueprint. + +``` + +The hostname of the app needs to be defined so that it is accessible via the ingress. We will also set the default route to be the root endpoint: + +```bash +juju config nginx-ingress-integrator \ + service-hostname=django-hello-world path-routes=/ +``` + +Monitor `juju status` until everything has a status of `active`. Use `curl http://django-hello-world --resolve django-hello-world:80:127.0.0.1` to send a request via the ingress. It should still be returning the `Hi!` greeting. + +```{note} + +The `-H "Host: django-hello-world"` option to the `curl` command is a way of setting the hostname of the request without setting a DNS record. + +``` + +We can now also change the Django allowed hosts to `django-hello-world` which is a production ready value (for production, you will need to setup a DNS record): + +```bash +juju config django-hello-world django-allowed-hosts=django-hello-world +``` + +Running `curl 127.0.0.1 -H "Host: django-hello-world"` should still get the Django app to respond with `Hi!`. + +## Tear things down + +You've reached the end of this tutorial. You have created a Django application, deployed it locally, build an OCI image for it and deployed it using Juju. Then we integrated it with PostgreSQL to be production ready, demonstrated how to add a root endpoint and how to configure the application and finally we exposed our application using an ingress. + +If you'd like to reset your working environment, you can run the following in the root directory for the tutorial: + +```bash +cd .. +deactivate +rm -rf charm .venv django_hello_world +# delete all the files created during the tutorial +rm django-hello-world_0.1_amd64.rock \ + django-hello-world_0.2_amd64.rock \ + django-hello-world_0.3_amd64.rock \ + rockcraft.yaml requirements.txt + +# Remove the juju model +juju destroy-model django-hello-world --destroy-storage +``` + +If you created an instance using Multipass, you can also clean it up. Start by exiting it: + +```bash +exit +``` + +And then you can proceed with its deletion: + +```bash +multipass delete charm-dev +multipass purge +``` + +## Next steps + +By the end of this tutorial you will have built a charm and evolved it in a number of typical ways. But there is a lot more to explore: + +| If you are wondering... | visit... | +|-------------------------|----------------------| +| "How do I...?" | {ref}`how-to-guides` | +| "What is...?" | {ref}`reference` | +| "Why...?", "So what?" | {ref}`explanation` | diff --git a/docs/tutorial/write-your-first-kubernetes-charm-for-a-fastapi-app.md b/docs/tutorial/write-your-first-kubernetes-charm-for-a-fastapi-app.md new file mode 100644 index 000000000..9fd71630f --- /dev/null +++ b/docs/tutorial/write-your-first-kubernetes-charm-for-a-fastapi-app.md @@ -0,0 +1,549 @@ +(write-your-first-kubernetes-charm-for-a-fastapi-app)= +# Write your first Kubernetes charm for a FastAPI app + +**What you’ll need:** + +- A workstation, e.g., a laptop, with amd64 architecture which has sufficient resources to launch a virtual machine with 4 CPUs, 4 GB RAM, and a 50 GB disk + - Note that a workstation with arm64 architecture can complete the majority of this tutorial. +- Familiarity with Linux +- About 90 minutes of free time. + +**What you’ll do:** + +Create a FastAPI application. Use that to create a rock with `rockcraft`. Use that to create a charm with `charmcraft`. Use that to test-deploy, configure, etc., your Django application on a local Kubernetes cloud, `microk8s`, with `juju`. All of that multiple times, mimicking a real development process. + +```{note} + +**rock** + +An Ubuntu LTS-based OCI compatible container image designed to meet security, stability, and reliability requirements for cloud-native software. + +**charm** + +A package consisting of YAML files + Python code that will automate every aspect of an application's lifecycle so it can be easily orchestrated with Juju. + +**`juju`** + +An orchestration engine for charmed applications. + +``` + + +```{important} + +**Should you get stuck or notice issues:** Please get in touch on [Matrix](https://matrix.to/#/#12-factor-charms:ubuntu.com) or [Discourse](https://discourse.charmhub.io/). + +``` + + +## Set things up + +Install Multipass. + +> 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: + +```bash +multipass launch --cpus 4 --disk 50G --memory 4G --name charm-dev 24.04 +``` + +Once the VM is up, open a shell into it: + +```bash +multipass shell charm-dev +``` + +In order to create the rock, you'll need to install Rockcraft: + +```bash +sudo snap install rockcraft --channel latest/edge --classic +``` + +`LXD` will be required for building the rock. Make sure it is installed and initialised: + +```bash +sudo snap install lxd +lxd init --auto +``` + +In order to create the charm, you'll need to install Charmcraft: + +```bash +sudo snap install charmcraft --channel latest/edge --classic +``` + +MicroK8s is required to deploy the FastAPI application on Kubernetes. Install MicroK8s: + +```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: + +```bash +sudo microk8s enable hostpath-storage +# Required to host the OCI image of the FastAPI application +sudo microk8s enable registry +# Required to expose the FastAPI application +sudo microk8s enable ingress +``` + +> See more: [ingress^](https://microk8s.io/docs/ingress) + +Juju is required to deploy the FastAPI application. Install Juju and bootstrap a development controller: + +```bash +sudo snap install juju --channel 3.5/stable +mkdir -p ~/.local/share +juju bootstrap microk8s dev-controller +``` + +Finally, create a new directory for this tutorial and go inside it: + +```bash +mkdir fastapi-hello-world +cd fastapi-hello-world +``` + +```{note} + +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. + +This tutorial requires version `1.5.4` or later of Rockcraft. Check the version of Rockcraft using `rockcraft --version` If you have an older version of Rockcraft installed, use `sudo snap refresh rockcraft --channel latest/edge` to get the latest edge version of Rockcraft. + +``` + +## Create the FastAPI application + +Start by creating the "Hello, world" FastAPI application that will be used for this tutorial. + +Create a `requirements.txt` file, copy the following text into it and then save it: + +``` +fastapi[standard] +``` + +In the same directory, copy and save the following into a text file called `app.py`: + +```python +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +async def root(): + return {"message": "Hello World"} +``` + +## Run the FastAPI application locally + +Install `python3-venv` and create a virtual environment: + +```bash +sudo apt-get update && sudo apt-get install python3-venv -y +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +Now that we have a virtual environment with all the dependencies, let's run the FastAPI application to verify that it works: + +```bash +fastapi dev app.py --port 8080 +``` + +Test the FastAPI application by using `curl` to send a request to the root endpoint. You may need a new terminal for this; if you are using Multipass use `multipass shell charm-dev` to get another terminal: + +```bash +curl localhost:8080 +``` + +The FastAPI application should respond with `{"message":"Hello World"}`. The FastAPI application looks good, so we can stop for now using ctrl + c. + +## Pack the FastAPI application into a rock + +First, we'll need a `rockcraft.yaml` file. Rockcraft will automate its creation and tailoring for a FastAPI application by using the `fastapi-framework` profile: + +```bash +rockcraft init --profile fastapi-framework +``` + +The `rockcraft.yaml` file will automatically be created and set the name based on your working directory. Open the file in a text editor and check that the `name` is `fastapi-hello-world`. 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` "fastapi-hello-world" and assume you are on the `amd64` platform. Check the architecture of your system using `dpkg --print-architecture`. Choosing a different name or running on a different platform will influence the names of the files generated by Rockcraft. + +``` + +Pack the rock: + +```bash +ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack +``` + +```{note} + +Depending on your system and network, this step can take a couple of minutes to finish. + +``ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS`` is required whilst the FastAPI extension is experimental. + +``` + +Once Rockcraft has finished packing the FastAPI rock, you'll find a new file in your working directory with the `.rock` extension: + +```bash +ls *.rock -l +``` + +```{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. + +``` + +The rock needs to be copied to the MicroK8s registry so that it can be deployed in the Kubernetes cluster: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:fastapi-hello-world_0.1_amd64.rock \ + docker://localhost:32000/fastapi-hello-world:0.1 +``` + +> See more: [skopeo^](https://manpages.ubuntu.com/manpages/jammy/man1/skopeo.1.html) + +## Create the charm + +Create a new directory for the charm and go inside it: + +```bash +mkdir charm +cd charm +``` + +We'll need a `charmcraft.yaml`, `requirements.txt` and source code for the charm. The source code contains the logic required to operate the FastAPI application. Charmcraft will automate the creation of these files by using the `fastapi-framework` profile: + +```bash +charmcraft init --profile fastapi-framework --name fastapi-hello-world +``` + +The files will automatically be created in your working directory. + +The charm depends on several libraries. Download the libraries and pack the charm: + +```bash +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft fetch-libs +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack +``` + +```{note} + +Depending on your system and network, this step can take a couple of minutes to finish. + +``CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS`` is required whilst the FastAPI extension is experimental. + +``` + +Once Charmcraft has finished packing the charm, you'll find a new file in your working directory with the `.charm` extension: + +```bash +ls *.charm -l +``` + +```{note} + +If you changed the name in charmcraft.yaml or are not on the amd64 platform, the name of the `.charm` file will be different for you. + +``` + +## Deploy the FastAPI application + +A Juju model is needed to deploy the application. Let's create a new model: + +```bash +juju add-model fastapi-hello-world +``` + +Now the FastAPI application can be deployed using Juju: + +```bash +juju deploy ./fastapi-hello-world_amd64.charm fastapi-hello-world \ + --resource app-image=localhost:32000/fastapi-hello-world:0.1 +``` + +```{note} + +It will take a few minutes to deploy the FastAPI application. You can monitor the progress using `juju status --watch 5s`. Once the status of the App has gone to `active`, you can stop watching using ctrl + c. + +> See more: {ref}`Command 'juju status' ` + +``` + +The FastAPI application should now be running. We can monitor the status of the deployment using `juju status` which should be similar to the following output: + +``` +Model Controller Cloud/Region Version SLA Timestamp +fastapi-hello-world dev-controller microk8s/localhost 3.5.4 unsupported 13:45:18+10:00 + +App Version Status Scale Charm Channel Rev Address Exposed Message +fastapi-hello-world active 1 fastapi-hello-world 0 10.152.183.53 no + +Unit Workload Agent Address Ports Message +fastapi-hello-world/0* active idle 10.1.157.75 +``` + +The deployment is finished when the status shows `active`. Let's expose the application using ingress. Deploy the `nginx-ingress-integrator` charm and integrate it with the FastAPI app: + +```bash +juju deploy nginx-ingress-integrator +juju integrate nginx-ingress-integrator fastapi-hello-world +``` + +The hostname of the app needs to be defined so that it is accessible via the ingress. We will also set the default route to be the root endpoint: + +```bash +juju config nginx-ingress-integrator \ + service-hostname=fastapi-hello-world path-routes=/ +``` + +Monitor `juju status` until everything has a status of `active`. Use `curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1` to send a request via the ingress. It should return the `{"message":"Hello World"}` greeting. + +```{note} + +The `--resolve fastapi-hello-world:80:127.0.0.1` option to the `curl` command is a way of resolving the hostname of the request without setting a DNS record. + +``` + +## Configure the FastAPI application + +Now let's customise the greeting using a configuration option. We will expect this configuration option to be available in the environment variable `APP_GREETING`. Go back out to the root directory of the project using `cd ..` and copy the following code into `app.py`: + +```python +import os + +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +async def root(): + return {"message": os.getenv("APP_GREETING", "Hello World")} +``` + +Open `rockcraft.yaml` and update the version to `0.2`. Run `ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack` again, then upload the new OCI image to the MicroK8s registry: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:fastapi-hello-world_0.2_amd64.rock \ + docker://localhost:32000/fastapi-hello-world:0.2 +``` + +Change back into the charm directory using `cd charm`. The `fastapi-framework` Charmcraft extension supports adding configurations to `charmcraft.yaml` which will be passed as environment variables to the FastAPI application. Add the following to the end of the `charmcraft.yaml` file: + +```yaml +config: + options: + greeting: + description: | + The greeting to be returned by the FastAPI application. + default: "Hello, world!" + type: string +``` + +```{note} + +Configuration options are automatically capitalised and `-` are replaced by `_`. A `APP_` prefix will also be added to ensure that environment variables are namespaced. + +``` + +Run `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack` again. The deployment can now be refreshed to make use of the new code: + +```bash +juju refresh fastapi-hello-world \ + --path=./fastapi-hello-world_amd64.charm \ + --resource app-image=localhost:32000/fastapi-hello-world:0.2 +``` + +Wait for `juju status` to show that the App is `active` again. Verify that the new configuration has been added using `juju config fastapi-hello-world | grep -A 6 greeting:` which should show the configuration option. + +```{note} + +The `grep` command extracts a portion of the configuration to make it easier to check whether the configuration option has been added. + +``` + +Running `curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1` shows that the response is still `{"message":"Hello, world!"}` as expected. The greeting can be changed using Juju: + +```bash +juju config fastapi-hello-world greeting='Hi!' +``` + +`curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1` now returns the updated `{"message":"Hi!"}` greeting. + +```{note} + +It might take a short time for the configuration to take effect. + +``` + +## Integrate with a database + +Now let's keep track of how many visitors your application has received. This will require integration with a database to keep the visitor count. This will require a few changes: + +* We will need to create a database migration that creates the `visitors` table +* We will need to keep track how many times the root endpoint has been called in the database +* We will need to add a new endpoint to retrieve the number of visitors from the database + +The charm created by the `fastapi-framework` extension will execute the `migrate.py` script if it exists. This script should ensure that the database is initialised and ready to be used by the application. We will create a `migrate.py` file containing this logic. + +Go back out to the tutorial root directory using `cd ..`. Create the `migrate.py` file using a text editor and paste the following code into it: + +```python +import os + +import psycopg2 + + +DATABASE_URI = os.environ{ref}`"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() +``` + +```{note} + +The charm will pass the Database connection string in the `POSTGRESQL_DB_CONNECT_STRING` environment variable once postgres has been integrated with the charm. + +``` + +Open the `rockcraft.yaml` file in a text editor and update the version to `0.3`. + +To be able to connect to postgresql from the FastAPI app the `psycopg2-binary` dependency needs to be added in `requirements.txt`. The app code also needs to be updated to keep track of the number of visitors and to include a new endpoint to retrieve the number of visitors to the app. Open `app.py` in a text editor and replace its contents with the following code: + +```python +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} +``` + +Run `ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack` and upload the newly created rock to the MicroK8s registry: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:fastapi-hello-world_0.3_amd64.rock \ + docker://localhost:32000/fastapi-hello-world:0.3 +``` + +The FastAPI app now requires a database which needs to be declared in the `charmcraft.yaml` file. Go back into the charm directory using `cd charm`. Open `charmcraft.yaml` in a text editor and add the following section to the end: + +```yaml +requires: + postgresql: + interface: postgresql_client + optional: false +``` + +Pack the charm using `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack` and refresh the deployment using Juju: + +```bash +juju refresh fastapi-hello-world \ + --path=./fastapi-hello-world_amd64.charm \ + --resource app-image=localhost:32000/fastapi-hello-world:0.3 +``` + +Deploy `postgresql-k8s` using Juju and integrate it with `fastapi-hello-world`: + +```bash +juju deploy postgresql-k8s --trust +juju integrate fastapi-hello-world postgresql-k8s +``` + +Wait for `juju status` to show that the App is `active` again. `curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1` should still return the `{"message":"Hi!"}` greeting. To check the total visitors, use `curl http://fastapi-hello-world/visitors --resolve fastapi-hello-world:80:127.0.0.1` which should return `{"count":1}` after the previous request to the root endpoint and should be incremented each time the root endpoint is requested. If we perform another request to `curl http://fastapi-hello-world --resolve fastapi-hello-world:80:127.0.0.1`, `curl http://fastapi-hello-world/visitors --resolve fastapi-hello-world:80:127.0.0.1` will return `{"count":2}`. + +## Tear things down + +We've reached the end of this tutorial. We have created a FastAPI application, deployed it locally, integrated it with a database and exposed it via ingress! + +If you'd like to reset your working environment, you can run the following in the root directory for the tutorial: + +```bash +# 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_amd64.rock fastapi-hello-world_0.2_amd64.rock \ + fastapi-hello-world_0.3_amd64.rock rockcraft.yaml app.py \ + requirements.txt migrate.py +# Remove the juju model +juju destroy-model fastapi-hello-world --destroy-storage +``` + +If you created an instance using Multipass, you can also clean it up. Start by exiting it: + +```bash +exit +``` + +And then you can proceed with its deletion: + +```bash +multipass delete charm-dev +multipass purge +``` + + +## Next steps + +By the end of this tutorial you will have built a charm and evolved it in a number of typical ways. But there is a lot more to explore: + +| If you are wondering... | visit... | +|-------------------------|----------------------| +| "How do I...?" | {ref}`how-to-guides` | +| "What is...?" | {ref}`reference` | +| "Why...?", "So what?" | {ref}`explanation` | diff --git a/docs/tutorial/write-your-first-kubernetes-charm-for-a-go-app.md b/docs/tutorial/write-your-first-kubernetes-charm-for-a-go-app.md new file mode 100644 index 000000000..a353f2732 --- /dev/null +++ b/docs/tutorial/write-your-first-kubernetes-charm-for-a-go-app.md @@ -0,0 +1,620 @@ +(write-your-first-kubernetes-charm-for-a-go-app)= +# Write your first Kubernetes charm for a Go app + + +**What you’ll need:** + +- A working station, e.g., a laptop, with amd64 architecture which has sufficient resources to launch a virtual machine with 4 CPUs, 4 GB RAM, and a 50 GB disk + - Note that a workstation with arm64 architecture can complete the majority of this tutorial. +- Familiarity with Linux. +- About 90 minutes of free time. + +**What you’ll do:** + +Create a Go application. Use that to create a rock with `rockcraft`. Use that to create a charm with `charmcraft`. Use that to test-deploy, configure, etc., your Django application on a local Kubernetes cloud, `microk8s`, with `juju`. All of that multiple times, mimicking a real development process. + + +```{note} + +**rock** + +An Ubuntu LTS-based OCI compatible container image designed to meet security, stability, and reliability requirements for cloud-native software. + +**charm** + +A package consisting of YAML files + Python code that will automate every aspect of an application's lifecycle so it can be easily orchestrated with Juju. + +**`juju`** + +An orchestration engine for charmed applications. + +``` + + +```{important} + +**Should you get stuck or notice issues:** Please get in touch on [Matrix](https://matrix.to/#/#12-factor-charms:ubuntu.com) or [Discourse](https://discourse.charmhub.io/). + +``` + + +## Set things up + +Install Multipass. + +> 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 22.04 blueprint: + +```bash +multipass launch --cpus 4 --disk 50G --memory 4G --name charm-dev 22.04 +``` + +Once the VM is up, open a shell into it: + +```bash +multipass shell charm-dev +``` + +In order to create the rock, you'll need to install Rockcraft: + +```bash +sudo snap install rockcraft --channel latest/edge --classic +``` + +`LXD` will be required for building the rock. Make sure it is installed and initialised: + +```bash +sudo snap install lxd +lxd init --auto +``` + +In order to create the charm, you'll need to install Charmcraft: + +```bash +sudo snap install charmcraft --channel latest/edge --classic +``` + +MicroK8s is required to deploy the Go application on Kubernetes. Install MicroK8s: + +```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: + +```bash +sudo microk8s enable hostpath-storage +# Required to host the OCI image of the Go application +sudo microk8s enable registry +# Required to expose the Go application +sudo microk8s enable ingress +``` + +> See more: [ingress^](https://microk8s.io/docs/ingress) + +Juju is required to deploy the Go application. Install Juju and bootstrap a development controller: + +```bash +sudo snap install juju --channel 3.5/stable +mkdir -p ~/.local/share +juju bootstrap microk8s dev-controller +``` + +Finally, create a new directory for this tutorial and go inside it: + +```bash +mkdir go-hello-world +cd go-hello-world +``` + +```{note} + +This tutorial requires version `3.2.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. + +This tutorial requires version `1.5.4` or later of Rockcraft. Check the version of Rockcraft using `rockcraft --version` If you have an older version of Rockcraft installed, use `sudo snap refresh rockcraft --channel latest/edge` to get the latest edge version of Rockcraft. + +``` + +## Create the Go application + +Start by creating the "Hello, world" Go application that will be used for this tutorial. + +Install `go` and initialise the Go module: +```bash +sudo snap install go --classic +go mod init go-hello-world +``` + +Create a `main.go` file, copy the following text into it and then save it: + +```go +package main + +import ( + "fmt" + "log" + "net/http" +) + +func helloWorldHandler(w http.ResponseWriter, req *http.Request) { + log.Printf("new hello world request") + fmt.Fprintln(w, "Hello, world!") +} + +func main() { + log.Printf("starting hello world application") + http.HandleFunc("/", helloWorldHandler) + http.ListenAndServe(":8080", nil) +} +``` + +## Run the Go application locally + +Build the Go application so it can be run: + +```bash +go build . +``` + +Now that we have a binary compiled, let's run the Go application to verify that it works: + +```bash +./go-hello-world +``` + +Test the Go application by using `curl` to send a request to the root endpoint. You may need a new terminal for this; if you are using Multipass use `multipass shell charm-dev` to get another terminal: + +```bash +curl localhost:8080 +``` + +The Go application should respond with `Hello, world!`. The Go application looks good, so we can stop for now using ctrl + c. + +## Pack the Go application into a rock + +First, we'll need a `rockcraft.yaml` file. Rockcraft will automate its creation and tailoring for a Go application by using the `go-framework` profile: + +```bash +rockcraft init --profile go-framework +``` + +The `rockcraft.yaml` file will automatically be created and set the +name based on your working directory. Open the file in a text editor +and check that the `name` is `go-hello-world`. 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` "go-hello-world" and assume you are on the `amd64` platform. Check the architecture of your system using `dpkg --print-architecture`. Choosing a different name or running on a different platform will influence the names of the files generated by Rockcraft. + +``` + +Pack the rock: + +```bash +ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack +``` + +```{note} + +Depending on your system and network, this step can take a couple of minutes to finish. + +``` + +Once Rockcraft has finished packing the Go rock, you'll find a new file in your working directory with the `.rock` extension: + +```bash +ls *.rock -l +``` + +```{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. + +``` + +The rock needs to be copied to the Microk8s registry so that it can be deployed in the Kubernetes cluster: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:go-hello-world_0.1_amd64.rock \ + docker://localhost:32000/go-hello-world:0.1 +``` + +> See more: [skopeo^](https://manpages.ubuntu.com/manpages/jammy/man1/skopeo.1.html) + +## Create the charm + +Create a new directory for the charm and go inside it: + +```bash +mkdir charm +cd charm +``` + +We'll need a `charmcraft.yaml`, `requirements.txt` and source code for the charm. The source code contains the logic required to operate the Go application. Charmcraft will automate the creation of these files by using the `go-framework` profile: + +```bash +charmcraft init --profile go-framework --name go-hello-world +``` + +The files will automatically be created in your working directory. + +The charm depends on several libraries. Download the libraries and pack the charm: + +```bash +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft fetch-libs +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack +``` + +```{note} + +Depending on your system and network, this step can take a couple of minutes to finish. + +``` + +Once Charmcraft has finished packing the charm, you'll find a new file in your working directory with the `.charm` extension: + +```bash +ls *.charm -l +``` + +```{note} + +If you changed the name in charmcraft.yaml or are not on the amd64 platform, the name of the `.charm` file will be different for you. + +``` + +## Deploy the Go application + +A Juju model is needed to deploy the application. Let's create a new model: + +```bash +juju add-model go-hello-world +``` + +```{note} + +If you are not on a host with the amd64 architecture, you will need to include a constraint to the Juju model to specify your architecture. For example, for the arm64 architecture, use `juju set-model-constraints -m go-hello-world arch=arm64`. Check the architecture of your system using `dpkg --print-architecture`. + +``` + +Now the Go application can be deployed using Juju: + +```bash +juju deploy ./go-hello-world_amd64.charm \ + go-hello-world \ + --resource app-image=localhost:32000/go-hello-world:0.1 +``` + +```{note} + +It will take a few minutes to deploy the Go application. You can monitor the progress using `juju status --watch 5s`. Once the status of the App has gone to `active`, you can stop watching using ctrl + c. + +> See more: {ref}`Command 'juju status' ` + +``` + +The Go application should now be running. We can monitor the status of the deployment using `juju status` which should be similar to the following output: + +``` +go-hello-world microk8s microk8s/localhost 3.5.4 unsupported 14:35:07+02:00 + +App Version Status Scale Charm Channel Rev Address Exposed Message +go-hello-world active 1 go-hello-world 0 10.152.183.229 no + +Unit Workload Agent Address Ports Message +go-hello-world/0* active idle 10.1.157.79 +``` + +The deployment is finished when the status shows `active`. Let's expose the application using ingress. Deploy the `nginx-ingress-integrator` charm and integrate it with the Go app: + +```bash +juju deploy nginx-ingress-integrator --trust +juju integrate nginx-ingress-integrator go-hello-world +``` + +The hostname of the app needs to be defined so that it is accessible via the ingress. We will also set the default route to be the root endpoint: + +```bash +juju config nginx-ingress-integrator \ + service-hostname=go-hello-world path-routes=/ +``` + +```{note} + +By default, the port for the Go application should be 8080. If you want to change the default port, it can be done +with the configuration option `app-port` that will be exposed as `APP_PORT` to the Go application. + +``` + +Monitor `juju status` until everything has a status of `active`. Use `curl http://go-hello-world --resolve go-hello-world:80:127.0.0.1` to send a request via the ingress. The Go application should respond with `Hello, world!`. + +## Configure the Go application + +Now let's customise the greeting using a configuration option. We will expect this configuration option to be available in the Go app configuration under the keyword `GREETING`. Go back out to the root directory of the project using `cd ..` and copy the following code into `main.go`: + +```python +package main + +import ( + "fmt" + "log" + "os" + "net/http" +) + +func helloWorldHandler(w http.ResponseWriter, req *http.Request) { + log.Printf("new hello world request") + greeting, found := os.LookupEnv("APP_GREETING") + if !found { + greeting = "Hello, world!" + } + fmt.Fprintln(w, greeting) +} + +func main() { + log.Printf("starting hello world application") + http.HandleFunc("/", helloWorldHandler) + http.ListenAndServe(":8080", nil) +} +``` + +Open `rockcraft.yaml` and update the version to `0.2`. Run `ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack` again, then upload the new OCI image to the MicroK8s registry: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:go-hello-world_0.2_amd64.rock \ + docker://localhost:32000/go-hello-world:0.2 +``` + +Change back into the charm directory using `cd charm`. The `go-framework` Charmcraft extension supports adding configurations to `charmcraft.yaml` which will be passed as environment variables to the Go application. Add the following to the end of the `charmcraft.yaml` file: + +```yaml +config: + options: + greeting: + description: | + The greeting to be returned by the Go application. + default: "Hello, world!" + type: string +``` + +```{note} + +Configuration options are automatically capitalised and `-` are replaced by `_`. A `APP_` prefix will also be added. + +``` + +Run `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack` again. The deployment can now be refreshed to make use of the new code: + +```bash +juju refresh go-hello-world \ + --path=./go-hello-world_amd64.charm \ + --resource app-image=localhost:32000/go-hello-world:0.2 +``` + +Wait for `juju status` to show that the App is `active` again. Verify that the new configuration has been added using `juju config go-hello-world | grep -A 6 greeting:` which should show the configuration option. + +```{note} + +The `grep` command extracts a portion of the configuration to make it easier to check whether the configuration option has been added. + +``` + +Using `curl http://go-hello-world --resolve go-hello-world:80:127.0.0.1` shows that the response is still `Hello, world!` as expected. The greeting can be changed using Juju: + +```bash +juju config go-hello-world greeting='Hi!' +``` + +`curl http://go-hello-world --resolve go-hello-world:80:127.0.0.1` now returns the updated `Hi!` greeting. + +```{note} + +It might take a short time for the configuration to take effect. + +``` + +## Integrate with a database + +Now let's keep track of how many visitors your application has received. This will require integration with a database to keep the visitor count. This will require a few changes: + +* We will need to create a database migration that creates the `visitors` table +* We will need to keep track how many times the root endpoint has been called in the database +* We will need to add a new endpoint to retrieve the number of visitors from the database + +The charm created by the `go-framework` extension will execute the `migrate.sh` script if it exists. This script should ensure that the database is initialised and ready to be used by the application. We will create a `migrate.sh` file containing this logic. + +Go back out to the tutorial root directory using `cd ..`. Create the `migrate.sh` file using a text editor and paste the following code into it: + +```bash +#!/bin/bash + +PGPASSWORD="${POSTGRESQL_DB_PASSWORD}" psql -h "${POSTGRESQL_DB_HOSTNAME}" -U "${POSTGRESQL_DB_USERNAME}" "${POSTGRESQL_DB_NAME}" -c "CREATE TABLE IF NOT EXISTS visitors (timestamp TIMESTAMP NOT NULL, user_agent TEXT NOT NULL);" +``` + +```{note} + +The charm will pass the Database connection string in the `POSTGRESQL_DB_CONNECT_STRING` environment variable once PostgreSQL has been integrated with the charm. + +``` + +Change the permissions of the file `migrate.sh` so it is executable: +```bash +chmod u+x migrate.sh +``` + +For the migrations to work we need the `postgresql-client` package +installed in the rock. As by default the `go-framework` uses the `base` +base, we would also need to install a shell interpreter. Let's do it +as a slice, so the rock does not include unnecessary files. Open the `rockcraft.yaml` file using a text editor, update the version to `0.3` and add the +following to the end: +```yaml +parts: + runtime-debs: + plugin: nil + stage-packages: + # Added manually for the migrations + - postgresql-client + runtime-slices: + plugin: nil + stage-packages: + # Added manually for the migrations + - bash_bins +``` + +To be able to connect to PostgreSQL from the Go app the library `pgx` will be used. +The app code needs to be updated to keep +track of the number of visitors and to include a new endpoint to +retrieve the number of visitors to the app. Open `main.go` in a text +editor and replace its contents with the following code: + +```go +package main + +import ( + "database/sql" + "fmt" + "log" + "net/http" + "os" + "time" + + _ "github.com/jackc/pgx/v5/stdlib" +) + +func helloWorldHandler(w http.ResponseWriter, req *http.Request) { + log.Printf("new hello world request") + postgresqlURL := os.Getenv("POSTGRESQL_DB_CONNECT_STRING") + db, err := sql.Open("pgx", postgresqlURL) + if err != nil { + log.Printf("An error occurred while connecting to postgresql: %v", err) + return + } + defer db.Close() + + ua := req.Header.Get("User-Agent") + timestamp := time.Now() + _, err = db.Exec("INSERT into visitors (timestamp, user_agent) VALUES ($1, $2)", timestamp, ua) + if err != nil { + log.Printf("An error occurred while executing query: %v", err) + return + } + + greeting, found := os.LookupEnv("APP_GREETING") + if !found { + greeting = "Hello, world!" + } + + fmt.Fprintln(w, greeting) +} + +func visitorsHandler(w http.ResponseWriter, req *http.Request) { + log.Printf("visitors request") + postgresqlURL := os.Getenv("POSTGRESQL_DB_CONNECT_STRING") + db, err := sql.Open("pgx", postgresqlURL) + if err != nil { + return + } + defer db.Close() + + var numVisitors int + err = db.QueryRow("SELECT count(*) from visitors").Scan(&numVisitors) + if err != nil { + log.Printf("An error occurred while executing query: %v", err) + return + } + fmt.Fprintf(w, "Number of visitors %d\n", numVisitors) +} + +func main() { + log.Printf("starting hello world application") + http.HandleFunc("/", helloWorldHandler) + http.HandleFunc("/visitors", visitorsHandler) + http.ListenAndServe(":8080", nil) +} +``` + +Check all the packages and their dependencies in the Go project with the following command: +```bash +go mod tidy +``` + +Run `ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack` and upload the newly created rock to the MicroK8s registry: + +```bash +rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \ + oci-archive:go-hello-world_0.3_amd64.rock \ + docker://localhost:32000/go-hello-world:0.3 +``` + +Go back into the charm directory using `cd charm`. The Go app now requires a database which needs to be declared in the `charmcraft.yaml` file. Open `charmcraft.yaml` in a text editor and add the following section to the end: + +```yaml +requires: + postgresql: + interface: postgresql_client + optional: false +``` + +Pack the charm using `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack` and refresh the deployment using Juju: + +```bash +juju refresh go-hello-world \ + --path=./go-hello-world_amd64.charm \ + --resource app-image=localhost:32000/go-hello-world:0.3 +``` + +Deploy `postgresql-k8s` using Juju and integrate it with `go-hello-world`: + +```bash +juju deploy postgresql-k8s --trust +juju integrate go-hello-world postgresql-k8s +``` + +Wait for `juju status` to show that the App is `active` again. `curl http://go-hello-world --resolve go-hello-world:80:127.0.0.1` should still return the `Hi!` greeting. To check the total visitors, use `curl http://go-hello-world/visitors --resolve go-hello-world:80:127.0.0.1` which should return `Number of visitors 1` after the previous request to the root endpoint and should be incremented each time the root endpoint is requested. If we perform another request to `curl http://go-hello-world --resolve go-hello-world:80:127.0.0.1`, `curl http://go-hello-world/visitors --resolve go-hello-world:80:127.0.0.1` will return `Number of visitors 2`. + +## Tear things down + +We've reached the end of this tutorial. We have created a Go application, deployed it locally, integrated it with a database and exposed it via ingress! + +If you'd like to reset your working environment, you can run the following in the root directory for the tutorial: + +```bash +cd .. +rm -rf charm +# delete all the files created during the tutorial +rm go-hello-world_0.1_amd64.rock go-hello-world_0.2_amd64.rock \ + go-hello-world_0.3_amd64.rock rockcraft.yaml main.go \ + migrate.sh go-hello-world go.mod go.sum +# Remove the juju model +juju destroy-model go-hello-world --destroy-storage +``` + +If you created an instance using Multipass, you can also clean it up. Start by exiting it: + +```bash +exit +``` + +And then you can proceed with its deletion: + +```bash +multipass delete charm-dev +multipass purge +``` + +## Next steps + +By the end of this tutorial you will have built a charm and evolved it in a number of typical ways. But there is a lot more to explore: + +| If you are wondering... | visit... | +|-------------------------|----------------------| +| "How do I...?" | {ref}`how-to-guides` | +| "What is...?" | {ref}`reference` | +| "Why...?", "So what?" | {ref}`explanation` | From 5ed81118a201cf09e79f475cae1af3e6446b1864 Mon Sep 17 00:00:00 2001 From: Teodora Mihoc Date: Thu, 19 Dec 2024 16:12:37 +0100 Subject: [PATCH 02/19] docs: convert howtos to rst --- docs/common/craft-parts | 1 + docs/howto/index.md | 23 - docs/howto/index.rst | 22 + docs/howto/manage-bundles.md | 40 -- docs/howto/manage-bundles.rst | 40 ++ docs/howto/manage-channels.md | 75 --- docs/howto/manage-channels.rst | 86 +++ docs/howto/manage-charms.md | 507 ---------------- docs/howto/manage-charms.rst | 565 ++++++++++++++++++ docs/howto/manage-extensions.md | 209 ------- docs/howto/manage-extensions.rst | 208 +++++++ docs/howto/manage-icons.md | 70 --- docs/howto/manage-icons.rst | 83 +++ docs/howto/manage-libraries.md | 111 ---- docs/howto/manage-libraries.rst | 111 ++++ docs/howto/manage-names.md | 36 -- docs/howto/manage-names.rst | 43 ++ docs/howto/manage-parts.md | 48 -- docs/howto/manage-parts.rst | 47 ++ docs/howto/manage-resources.md | 97 --- docs/howto/manage-resources.rst | 106 ++++ docs/howto/manage-revisions.md | 81 --- docs/howto/manage-revisions.rst | 79 +++ docs/howto/manage-the-charmcraft-cli.md | 119 ---- docs/howto/manage-the-charmcraft-cli.rst | 138 +++++ .../howto/manage-the-current-charmhub-user.md | 99 --- .../manage-the-current-charmhub-user.rst | 128 ++++ docs/howto/manage-tracks.md | 101 ---- docs/howto/manage-tracks.rst | 104 ++++ docs/howto/misc/index.md | 16 - docs/howto/misc/index.rst | 12 + ...ack-a-hooks-based-charm-with-charmcraft.md | 158 ----- ...ck-a-hooks-based-charm-with-charmcraft.rst | 106 ++++ ...-a-reactive-based-charm-with-charmcraft.md | 126 ---- ...a-reactive-based-charm-with-charmcraft.rst | 33 + docs/reference/charmcraft-cli.md | 7 - docs/reference/charmcraft-cli.rst | 10 + docs/reference/files/file-charmcraft-yaml.md | 1 + .../list-of-charmcraft-cli-commands.rst | 2 +- 39 files changed, 1924 insertions(+), 1924 deletions(-) create mode 120000 docs/common/craft-parts delete mode 100644 docs/howto/index.md create mode 100644 docs/howto/index.rst delete mode 100644 docs/howto/manage-bundles.md create mode 100644 docs/howto/manage-bundles.rst delete mode 100644 docs/howto/manage-channels.md create mode 100644 docs/howto/manage-channels.rst delete mode 100644 docs/howto/manage-charms.md create mode 100644 docs/howto/manage-charms.rst delete mode 100644 docs/howto/manage-extensions.md create mode 100644 docs/howto/manage-extensions.rst delete mode 100644 docs/howto/manage-icons.md create mode 100644 docs/howto/manage-icons.rst delete mode 100644 docs/howto/manage-libraries.md create mode 100644 docs/howto/manage-libraries.rst delete mode 100644 docs/howto/manage-names.md create mode 100644 docs/howto/manage-names.rst delete mode 100644 docs/howto/manage-parts.md create mode 100644 docs/howto/manage-parts.rst delete mode 100644 docs/howto/manage-resources.md create mode 100644 docs/howto/manage-resources.rst delete mode 100644 docs/howto/manage-revisions.md create mode 100644 docs/howto/manage-revisions.rst delete mode 100644 docs/howto/manage-the-charmcraft-cli.md create mode 100644 docs/howto/manage-the-charmcraft-cli.rst delete mode 100644 docs/howto/manage-the-current-charmhub-user.md create mode 100644 docs/howto/manage-the-current-charmhub-user.rst delete mode 100644 docs/howto/manage-tracks.md create mode 100644 docs/howto/manage-tracks.rst delete mode 100644 docs/howto/misc/index.md create mode 100644 docs/howto/misc/index.rst delete mode 100644 docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md create mode 100644 docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst delete mode 100644 docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md create mode 100644 docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst delete mode 100644 docs/reference/charmcraft-cli.md create mode 100644 docs/reference/charmcraft-cli.rst diff --git a/docs/common/craft-parts b/docs/common/craft-parts new file mode 120000 index 000000000..4aa111d9f --- /dev/null +++ b/docs/common/craft-parts @@ -0,0 +1 @@ +/home/dora/git/charmcraft/.tox/docs/lib/python3.12/site-packages/craft_parts_docs/craft-parts \ No newline at end of file diff --git a/docs/howto/index.md b/docs/howto/index.md deleted file mode 100644 index f4e977f3c..000000000 --- a/docs/howto/index.md +++ /dev/null @@ -1,23 +0,0 @@ -(how-to-guides)= -# How-to guides - - -```{toctree} -:maxdepth: 2 - -Manage the `charmcraft` CLI -Manage charms -Manage charms (12-factor apps) -Manage extensions -Manage resources -Manage libraries -Manage parts -Manage the current Charmhub user -Manage names -Manage revisions -Manage channels -Manage tracks -Manage icons -Misc -Manage bundles -``` diff --git a/docs/howto/index.rst b/docs/howto/index.rst new file mode 100644 index 000000000..a3cd99c29 --- /dev/null +++ b/docs/howto/index.rst @@ -0,0 +1,22 @@ +.. _how-to-guides: +How-to guides +============= + +.. toctree:: + :maxdepth: 2 + + Manage the charmcraft CLI + Manage charms + Manage charms (12-factor apps) + Manage extensions + Manage resources + Manage libraries + Manage parts + Manage the current Charmhub user + Manage names + Manage revisions + Manage channels + Manage tracks + Manage icons + Misc + Manage bundles diff --git a/docs/howto/manage-bundles.md b/docs/howto/manage-bundles.md deleted file mode 100644 index cacceafcd..000000000 --- a/docs/howto/manage-bundles.md +++ /dev/null @@ -1,40 +0,0 @@ -(how-to-manage-charm-bundles)= -# How to manage charm bundles - -> See first: [`juju` | Bundle](https://juju.is/docs/juju/bundle) - -```{important} -Starting with 1 Jan 2025, bundles are being phased out. - -``` - -## Create a bundle - -To create a bundle, create a `.yaml` file with your desired configuration. - -```{tip} - -If you don't want to start from scratch, export the contents of your model to a `.yaml` file via `juju export-bundle --filename .yaml` or download the `.yaml` of an existing bundle from Charmhub. - -> See more: [Juju | How to compare and export the contents of a model to a bundle](https://juju.is/docs/juju/manage-models#heading--compare-and-export-the-contents-of-a-model-to-a-bundle) - - -``` - -> See more: {ref}`file-bundle-yaml` - -## Pack a bundle - -To pack a bundle, in the directory where you have your `bundle.yaml` file (and possibly other files, e.g., a `README.md` file), create a `charmcraft.yaml` file suitable for a bundle (at the minimum: `type: bundle`), then run `charmcraft pack` to pack the bundle. The result is a `.zip` file. - -> See more: {ref}`ref_commands_pack` - - -## Publish a bundle on Charmhub - -The process is identical to that for a simple charm except that, at the step where you register the name, for bundles the command is `register-bundle`. - - -> See more: {ref}`publish-a-charm` - - diff --git a/docs/howto/manage-bundles.rst b/docs/howto/manage-bundles.rst new file mode 100644 index 000000000..c37f7b712 --- /dev/null +++ b/docs/howto/manage-bundles.rst @@ -0,0 +1,40 @@ +.. manage-charm-bundles: +How to manage charm bundles +=========================== + + See first: `Juju \| Bundle `__ + +.. important:: + Starting with 1 Jan 2025, bundles are being phased out. + + +Create a bundle +--------------- + +To create a bundle, create a ``.yaml`` file with your desired configuration. + + See more: :ref:`file-bundle-yaml` + +.. tip:: + If you don't want to start from scratch, export the contents of your model to a ``.yaml`` file via ``juju export-bundle --filename .yaml`` or download the ``.yaml`` of an existing bundle from Charmhub. See more: `Juju \| How to compare and export the contents of a model to a bundle `_. + + +Pack a bundle +------------- + +To pack a bundle, in the directory where you have your ``bundle.yaml`` +file (and possibly other files, e.g., a ``README.md`` file), create a +``charmcraft.yaml`` file suitable for a bundle (at the minimum: +``type: bundle``), then run ``charmcraft pack`` to pack the bundle. The +result is a ``.zip`` file. + + See more: :ref:`ref_commands_pack` + +Publish a bundle on Charmhub +---------------------------- + +The process is identical to that for a simple charm except that, at the +step where you register the name, for bundles the command is +``register-bundle``. + + See more: :ref:`publish-a-charm` diff --git a/docs/howto/manage-channels.md b/docs/howto/manage-channels.md deleted file mode 100644 index 10c132c27..000000000 --- a/docs/howto/manage-channels.md +++ /dev/null @@ -1,75 +0,0 @@ -(manage-channels)= -# How to manage channels - - -## Create a channel - -When you register a name on Charmhub, that automatically creates 4 channels, all with track `latest` but with different risk levels, namely, `edge`, `beta`, `candidate`, `stable`, respectively. - -> See more: {ref}`register-a-name` - - - -## View the available channels - -To view a charm's channels on Charmhub, run `charmcraft status` followed by the name of the charm. E.g., - -```bash -$ charmcraft status my-awesome-charm -Track Channel Version Revision -latest stable - - - candidate - - - beta 0.1 1 - edge ↑ ↑ -``` - -(The above output shows 4 channels, all of which have the same track, `latest`, but different risk levels, namely, `edge`, `beta`, `candidate`, and `stable`.) - -> See more: {ref}`command-charmcraft-status` - - -## Customise a channel's track - - -Request a track guardrail and create a track - -See {ref}`manage-tracks`. - - -## Open a channel - - -A channel is opened implicitly when you release a revision to it. - - - -## Close a channel - -A channel is opened when you release a revision to that channel. Before that, the channel is created but not opened. When you're closing a channel, e.g., latest/candidate, that means that any deployment requests that go there will be forwarded to the next most stable risk, e.g., for beta, latest/stable. If you close stable, you can no longer deploy or update from that, unless you release again to that channel (because releasing opens the channel). - -If you add a branch, closing that branch will forward people to the same track and risk, without a branch. diff --git a/docs/howto/manage-channels.rst b/docs/howto/manage-channels.rst new file mode 100644 index 000000000..30229a0b4 --- /dev/null +++ b/docs/howto/manage-channels.rst @@ -0,0 +1,86 @@ +.. _manage-channels: +How to manage channels +====================== + +Create a channel +---------------- + +When you register a name on Charmhub, that automatically creates 4 channels, all with track ``latest`` but with different risk levels, namely, ``edge``, ``beta``, ``candidate``, ``stable``, respectively. + + See more: :ref:`register-a-name` + +.. raw:: html + + + +View the available channels +--------------------------- + +To view a charm’s channels on Charmhub, run ``charmcraft status`` +followed by the name of the charm. E.g., + +.. code:: text + + $ charmcraft status my-awesome-charm + Track Channel Version Revision + latest stable - - + candidate - - + beta 0.1 1 + edge ↑ ↑ + +(The above output shows 4 channels, all of which have the same track, +``latest``, but different risk levels, namely, ``edge``, ``beta``, +``candidate``, and ``stable``.) + + See more: :ref:`ref_commands_status` + +Customise a channel’s track +--------------------------- + +You can request a track guardrail and create a track. + + See more: :ref:`manage-tracks` + +Open a channel +-------------- + +A channel is opened implicitly when you release a revision to it. + +Close a channel +--------------- + +A channel is opened when you release a revision to that channel. Before +that, the channel is created but not opened. When you’re closing a +channel, e.g., latest/candidate, that means that any deployment requests +that go there will be forwarded to the next most stable risk, e.g., for +beta, latest/stable. If you close stable, you can no longer deploy or +update from that, unless you release again to that channel (because +releasing opens the channel). + +If you add a branch, closing that branch will forward people to the same +track and risk, without a branch. diff --git a/docs/howto/manage-charms.md b/docs/howto/manage-charms.md deleted file mode 100644 index 09a7f8ccb..000000000 --- a/docs/howto/manage-charms.md +++ /dev/null @@ -1,507 +0,0 @@ -(manage-charms)= -# How to manage charms - -> See first: [`juju` | Charm](https://juju.is/docs/juju/charmed-operator), [`juju` | Manage charms](https://juju.is/docs/juju/manage-charms-or-bundles) - - -## Initialise a charm - -To initialise a charm project, create a directory for your charm, enter it, then run `charmcraft init` with the `--profile` flag followed by a suitable profile name (for machine charms: `machine`; for Kubernetes charms: `kubernetes`, `simple`, or `flask-framework`); that will create all the necessary files and even prepopulate them with useful content. - -```text -charmcraft init --profile -``` - - -````{dropdown} See sample session - - -```bash -$ mkdir my-flask-app-k8s -$ cd my-flask-app-k8s/ -$ charmcraft init --profile flask-framework -Charmed operator package file and directory tree initialised. - -Now edit the following package files to provide fundamental charm metadata -and other information: - -charmcraft.yaml -src/charm.py -README.md - -$ ls -R -.: -charmcraft.yaml requirements.txt src - -./src: -charm.py - - -``` - -```` - - - -The command also allows you to not specify any profile (in that case you get the `simple` profile -- a Kubernetes profile with lots of scaffolding, suitable for beginners) and has flags that you can use to specify a different directory to operate in, a charm name different from the name of the root directory, etc. - -> See more: {ref}`ref_commands_revisions`, {ref}`profile`, {ref}`list-of-files-in-a-charm-project` -> -> See more: {ref}`manage-extensions` - -## Add charm project metadata, an icon, docs - - -### Specify that the project is a charm - -To specify that the project is a charm (as supposed to a bundle), in your `charmcraft.yaml` file set the `type` key to `charm`: - -```text -type: charm -``` - -### Specify a name - -To specify a pack-and-deploy name for your charm, in your charm's `charmcraft.yaml` file specify the `name` key. E.g., - -```yaml -name: traefik-k8s -``` - -> See more: {ref}`file-charmcraft-yaml-name` - -### Specify a title - -To specify a title for your charm's page on Charmhub, in your charm's `charmcraft.yaml` file specify a value for the `title` key. E.g., - -```yaml -title: | - Traefik Ingress Operator for Kubernetes -``` - -> See more: {ref}`file-charmcraft-yaml-title` - -### Add a summary - -To add a summary line for your charm, in your charm's `charmcraft.yaml` file specify a value for the `summary` key. E.g., - -```yaml -summary: | - A Juju charm to run a Traefik-powered ingress controller on Kubernetes. -``` - -> See more: {ref}`file-charmcraft-yaml-summary` - -### Add a description - -To add a longer description for your charm, in your charm's `charmcraft.yaml` file specify a value for the `description` key. E.g., - -```yaml -description: | - A Juju-operated Traefik operator that routes requests from the outside of a - Kubernetes cluster to Juju units and applications. - -``` - -> See more: {ref}`file-charmcraft-yaml-description` - -### Add contact information - -To add maintainer contact information for a charm, in your charm's `charmcraft.yaml` file specify a value for the `links.contact` key. E.g., - -```yaml -links: - contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont -``` - -> See more: {ref}`file-charmcraft-yaml-contact` - -### Add a link to source code - -To add a link to the source code for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.source` key. E.g., - -```yaml -links: - source: - - https://github.com/canonical/traefik-k8s-operator -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add a link to the bug tracker - -To add a link to the bug tracker for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.issues` key. E.g., - -```yaml -links: - issues: - - https://github.com/canonical/traefik-k8s-operator/issues -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add a link to the website - -If your charm has a website outside of Charmhub, to add a link to this website, in your charm's `charmcraft.yaml` file specify an item under the `links.website` key. E.g., - -```yaml -links: - website: - - https://charmed-kubeflow.io/ -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add docs and a link to the docs - -If you publish your charm on Charmhub, reference documentation about the charm's resources, actions, configurations, relations, and libraries is extracted automatically. However, you should also aim to add further docs, e.g., a tutorial, how-to guides, etc. To provide a link to these docs, in your charm's `charmcraft.yaml` file specify a value for the `links.documentation` key. Note that at present this must be a Discourse page. E.g., - -```yaml -links: - documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add terms of use - -To add terms of use for your charm, in your charm's `charmcraft.yaml` file specify a value for the `terms` key. E.g., - -```yaml -terms: -- Butterscotch is regal -- Cara is adorable -``` - -> See more: {ref}`file-charmcraft-yaml-terms` - - -### Add an icon - -See {ref}`manage-icons`. - - -## Add runtime details to a charm - -### Require a specific Juju version - -To require a specific Juju version for your charm, in your charm's `charmcraft.yaml` specify the `assumes` key. E.g., - -```yaml -assumes: - - juju >= 3.5 -``` - -> See more: {ref}`file-charmcraft-yaml-assumes` - -### Require a Kubernetes cloud - -To require a Kubernetes cloud for your charm, in your charm's `charmcraft.yaml` file specify the `assumes` key. E.g., - -```yaml -assumes: - - k8s-api -``` - -> See more: {ref}`file-charmcraft-yaml-assumes` - -### Require a specific base and platforms - -To require a specific base and platforms for your charm, in your charm's `charmcraft.yaml` file specify the `base`(,`build-base`,) and the `platforms keys. E.g., - -```{note} -In Charmcraft < 3.0 this was done via a single key: `bases`. - -``` - -```yaml -# The run time base, the base format is @, -# accepted bases are: -# - ubuntu@24.04 -base: -# The build time base, if not defined the base is also the build time -# base, in addition to valid bases, the build-base can be "devel" -# which would use the latest in development Ubuntu Series. -build-base: - -platforms: - # The supported platforms, may omit build-for if platform-name - # is a valid arch, valid architectures follow the Debian architecture names, - # accepted architectures are: - # - amd64 - # - arm64 - # - armhf - # - ppc64el - # - riscv64 - # - s390x - : - # The build time architecture - build-on: | - # The run time architecture - build-for: | -``` - -> See more: {ref}`file-charmcraft-yaml-base`, {ref}`build-base`, {ref}`file-charmcraft-yaml-platforms` - -### Specify container requirements - -To specify container requirements, in your charm's `charmcraft.yaml` file specify the `containers` key. - - -> See more: {ref}`file-charmcraft-yaml-containers` - - -### Specify associated resources - -See {ref}`manage-resources`. - -### Specify device requirements - -> See more: {ref}`file-charmcraft-yaml-devices` - -To specify container requirements, in your charm's `charmcraft.yaml` file specify the `devices` key. - -### Specify storage requirements - -To specify storage requirements, in your charm's `charmcraft.yaml` file specify the `storage` key. - -> See more: {ref}`file-charmcraft-yaml-storage` - -### Specify extra binding requirements - -To specify extra binding requirements, in your charm's `charmcraft.yaml` file specify the `extra-bindings` key. - -> See more: {ref}`file-charmcraft-yaml-extra-bindings` - -### Require subordinate deployment - -To require subordinate deployment for your charm (i.e., for it to be deployed to the same machine as another charm, called its 'principal'), in your charm's `charmcraft.yaml` file specify the `subordinate` key. - -> See more: {ref}`file-charmcraft-yaml-subordinate` - - -### Manage actions - -> See first: [`juju` | Action](https://juju.is/docs/juju/action), [`juju` | Manage actions](https://juju.is/docs/juju/manage-actions) - - -To declare an action in your charm, in your charm's `charmcraft.yaml` file specify the `actions` key. - -> See more: {ref}`file-charmcraft-yaml-actions` -> -> See next: [`ops` | Manage actions]() - - -### Manage configurations - -> See first: [`juju` | Application configuration](https://juju.is/docs/juju/configuration#heading--application-configuration), [`juju` | Manage applications > Configure](https://juju.is/docs/juju/manage-applications#configure-an-application) - -To declare a configuration option for your charm, in your charm's `charmcraft.yaml` specify the `config` key. - - -> See more: {ref}`file-charmcraft-yaml-config` -> -> See next: [`ops` | Manage configurations]() - - - -### Manage relations (integrations) - -> See first: [`juju` | Relation](https://juju.is/docs/juju/relation), [`juju` | Manage relations](https://juju.is/docs/juju/manage-relations) - -To declare a relation endpoint in your charm, in your charm's `charmcraft.yaml` specify the `peers`, `provides`, or `requires` key. - -> See more: {ref}`file-charmcraft-yaml-peers-provides-requires` -> -> See more: [`ops` | Manage relations (integrations)]() - - -### Specify necessary libs - -> See first: [`juju` | Library]() - - -See {ref}`manage-libraries`. - -### Manage secrets -> See first: [`juju` | User secret](https://juju.is/docs/juju/secret#heading--user) - -To make your charm capable of accepting a user secret, in your charm's `charmcraft.yaml` specify the `config` key with the `type` subkey set to `secret`. - -> See more: {ref}`file-charmcraft-yaml-config` -> -> See next: [`ops` | Manage secrets]() - -### Specify necessary parts - -See {ref}`manage-parts`. - -## Pack a charm - -To pack a charm directory, in the charm's root directory, run the command below: - -```text -charmcraft pack -``` - -This will fetch any dependencies (from PyPI, based on `requirements.txt`), compile any modules, check that all the key files are in place, and produce a compressed archive with the extension `.charm`. As you can verify, this archive is just a zip file with metadata and the operator code itself. - -````{dropdown} Expand to view a sample session for a charm called microsample-vm - - -```text -# Pack the charm: -~/microsample-vm$ charmcraft pack -Created 'microsample-vm_ubuntu-22.04-amd64.charm'. -Charms packed: - microsample-vm_ubuntu-22.04-amd64.charm - -# (Optional) Verify that this has created a .charm file in your charm's root directory: -~/microsample-vm$ ls -CONTRIBUTING.md charmcraft.yaml requirements.txt tox.ini -LICENSE microsample-vm_ubuntu-22.04-amd64.charm src -README.md pyproject.toml tests - -# (Optional) Verify that the .charm file is simply a zip file that contains -# everything you've packed plus any dependencies: -/microsample-vm$ unzip -l microsample-vm_ubuntu-22.04-amd64.charm | { head; tail;} -Archive: microsample-vm_ubuntu-22.04-amd64.charm - Length Date Time Name ---------- ---------- ----- ---- - 815 2023-12-05 12:12 README.md - 11337 2023-12-05 12:12 LICENSE - 250 2023-12-05 12:31 manifest.yaml - 102 2023-12-05 12:31 dispatch - 106 2023-12-01 14:59 config.yaml - 717 2023-12-05 12:31 metadata.yaml - 921 2023-12-05 12:26 src/charm.py - 817 2023-12-01 14:44 venv/setuptools/command/__pycache__/upload.cpython-310.pyc - 65175 2023-12-01 14:44 venv/setuptools/command/__pycache__/easy_install.cpython-310.pyc - 4540 2023-12-01 14:44 venv/setuptools/command/__pycache__/py36compat.cpython-310.pyc - 1593 2023-12-01 14:44 venv/setuptools/command/__pycache__/bdist_rpm.cpython-310.pyc - 6959 2023-12-01 14:44 venv/setuptools/command/__pycache__/sdist.cpython-310.pyc - 2511 2023-12-01 14:44 venv/setuptools/command/__pycache__/rotate.cpython-310.pyc - 2407 2023-12-01 14:44 venv/setuptools/extern/__init__.py - 2939 2023-12-01 14:44 venv/setuptools/extern/__pycache__/__init__.cpython-310.pyc ---------- ------- - 20274163 1538 files - - -``` - -```` - -The command has a number of flags that allow you to specify a different charm directory to pack, whether to force pack if there are linting errors, etc. - -> See more: {ref}`ref_commands_pack` - -```{caution} - -**If you've declared any resources :** This will *not* pack the resources. This means that, when you upload your charm to Charmhub (if you do), you will have to upload the resources separately. - -> See more: {ref}`manage-resources` - -``` - -```{important} - -When the charm is packed, a series of analyses and lintings will happen, you may receive warnings and even errors to help improve the quality of the charm. - -> See more: {ref}`Charmcraft analyzers and linters ` - -``` - -> See next: [`juju` | Deploy a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Debug a charm](), [`juju` | Update a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-local-charm) - -(publish-a-charm)= -## Publish a charm on Charmhub - -1. Log in to Charmhub: - -```text -charmcraft login -``` - -> See more: {ref}`manage-the-current-charmhub-user` - -2. Register your charm's name (the one you specified in `charmcraft.yaml` > `name`): - -```text -charmcraft register my-awesome-charm -``` - -> See more: {ref}`manage-names` - -```{note} -This automatically creates 4 channels, all with track latest but with different risk levels, namely, edge, beta, candidate, stable, respectively. See more: {ref}`manage-channels`. -``` - -3. Upload the charm to Charmhub: Use the `charmcraft upload` command followed by the your charm's filepath. E.g., if you are in the charm's root directory, - -```text -charmcraft upload my-awesome-charm.charm -Revision 1 of my-awesome-charm created -``` - -> See more: {ref}`ref_commands_upload` - -```{note} -Each time you upload a charm to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-charm-revisions`. -``` - - -4. If your charm has associated resources: These are not packed with the rest of the charm project, so you must upload them explicitly to Charmhub as well. For example: - -```text -$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin -Revision 1 created of resource 'someresource' for charm 'my-super-charm' -``` - -> See more: {ref}`manage-resources` - -```{note} -Each time you upload a resource to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-resource-revisions`. -``` - -5. Release the charm: To release a charm, release your revision of choice to the target release channel. E.g., - -```text -$ charmcraft release my-awesome-charm --revision=1 --channel=beta -Revision 1 of charm 'my-awesome-charm' released to beta -``` - -> See more: {ref}`manage-charm-revisions` - -```{note} - -This automatically opens the channel. See more: {ref}`manage-channels`. -``` - -> See next: [`juju` | Deploy a Charmub charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Update a Charmhub charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-charmhub-charm) - -```{tip} -To update the charm on Charmhub, repeat the upload and release steps. - -``` - - -```{important} - -Releasing a charm on Charmhub gives it a public URL. However, the charm will not appear in the Charmhub search results until it has passed formal review. To request formal review, reach out to the community to announce your charm and ask for a review by an experienced community member. - -> See more: [Discourse | review requests](https://discourse.charmhub.io/c/charmhub-requests/46) - - -Also, the point of publishing and having a charm publicly listed on Charmhub is so others can reuse it and potentially contribute to it as well. To publicize your charm: - -- [Write a Discourse post to announce your release.](https://discourse.charmhub.io/tags/c/announcements-and-community/33/none) - -- [Schedule a community workshop to demo your charm's capabilities.](https://discourse.charmhub.io/tag/community-workshop) - -- [Chat about it with your charmer friends.](https://matrix.to/#/#charmhub-charmdev:ubuntu.com) - - -``` - - - diff --git a/docs/howto/manage-charms.rst b/docs/howto/manage-charms.rst new file mode 100644 index 000000000..8d7c91c3b --- /dev/null +++ b/docs/howto/manage-charms.rst @@ -0,0 +1,565 @@ +.. _manage-charms: +How to manage charms +==================== + + See first: `Juju \| Charm `_, `Juju \| Manage charms `_ + +Initialise a charm +------------------ + +To initialise a charm project, create a directory for your charm, enter +it, then run ``charmcraft init`` with the ``--profile`` flag followed by +a suitable profile name (for machine charms: ``machine``; for Kubernetes +charms: ``kubernetes``, ``simple``, or ``flask-framework``); that will +create all the necessary files and even prepopulate them with useful +content. + +.. code:: text + + charmcraft init --profile + +.. dropdown:: Example session + + .. code:: text + + $ mkdir my-flask-app-k8s + $ cd my-flask-app-k8s/ + $ charmcraft init --profile flask-framework + Charmed operator package file and directory tree initialised. + + Now edit the following package files to provide fundamental charm metadata + and other information: + + charmcraft.yaml + src/charm.py + README.md + + $ ls -R + .: + charmcraft.yaml requirements.txt src + + ./src: + charm.py + + + :: + + + + +The command also allows you to not specify any profile (in that case you get the `simple` profile -- a Kubernetes profile with lots of scaffolding, suitable for beginners) and has flags that you can use to specify a different directory to operate in, a charm name different from the name of the root directory, etc. + + See more: :ref:`ref_commands_revisions`, :ref:`profile`, :ref:`list-of-files-in-a-charm-project` + + See more: :ref:`manage-extensions` + +Add charm project metadata, an icon, docs +----------------------------------------- + + +Specify that the project is a charm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To specify that the project is a charm (as supposed to a bundle), in your `charmcraft.yaml` file set the `type` key to `charm`: + +.. code:: yaml + + type: charm + + +Specify a name +~~~~~~~~~~~~~~ + +To specify a pack-and-deploy name for your charm, in your charm's `charmcraft.yaml` file specify the `name` key. E.g., + +.. code:: yaml + + name: traefik-k8s + +.. + + See more: :ref:`file-charmcraft-yaml-name` + +Specify a title +~~~~~~~~~~~~~~~ + +To specify a title for your charm's page on Charmhub, in your charm's `charmcraft.yaml` file specify a value for the `title` key. E.g., + +.. code:: yaml + + title: | + Traefik Ingress Operator for Kubernetes + +.. + + See more: :ref:`file-charmcraft-yaml-title` + +Add a summary +~~~~~~~~~~~~~ + +To add a summary line for your charm, in your charm's `charmcraft.yaml` file specify a value for the `summary` key. E.g., + +.. code:: yaml + + summary: | + A Juju charm to run a Traefik-powered ingress controller on Kubernetes. + +.. + + See more: :ref:`file-charmcraft-yaml-summary` + +Add a description +~~~~~~~~~~~~~~~~~ + +To add a longer description for your charm, in your charm's `charmcraft.yaml` file specify a value for the `description` key. E.g., + +.. code:: yaml + + description: | + A Juju-operated Traefik operator that routes requests from the outside of a + Kubernetes cluster to Juju units and applications. + +.. + + + See more: :ref:`file-charmcraft-yaml-description` + +Add contact information +~~~~~~~~~~~~~~~~~~~~~~~ + +To add maintainer contact information for a charm, in your charm's `charmcraft.yaml` file specify a value for the `links.contact` key. E.g., + +.. code:: yaml + + links: + contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont + +.. + + + See more: :ref:`file-charmcraft-yaml-contact` + +Add a link to source code +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To add a link to the source code for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.source` key. E.g., + +.. code:: yaml + + links: + source: + - https://github.com/canonical/traefik-k8s-operator + +.. + + See more: :ref:`file-charmcraft-yaml-links` + +Add a link to the bug tracker +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To add a link to the bug tracker for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.issues` key. E.g., + +.. code:: yaml + + links: + issues: + - https://github.com/canonical/traefik-k8s-operator/issues + +.. + + See more: :ref:`file-charmcraft-yaml-links` + +Add a link to the website +~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your charm has a website outside of Charmhub, to add a link to this website, in your charm's `charmcraft.yaml` file specify an item under the `links.website` key. E.g., + +.. code:: yaml + + links: + website: + - https://charmed-kubeflow.io/ + +.. + + See more: :ref:`file-charmcraft-yaml-links` + +Add docs and a link to the docs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you publish your charm on Charmhub, reference documentation about the charm's resources, actions, configurations, relations, and libraries is extracted automatically. However, you should also aim to add further docs, e.g., a tutorial, how-to guides, etc. To provide a link to these docs, in your charm's `charmcraft.yaml` file specify a value for the `links.documentation` key. Note that at present this must be a Discourse page. E.g., + +.. code:: yaml + + links: + documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 + + +.. + + See more: :ref:`file-charmcraft-yaml-links` + +Add terms of use +~~~~~~~~~~~~~~~~ + + To add terms of use for your charm, in your charm's `charmcraft.yaml` file specify a value for the `terms` key. E.g., + +.. code:: yaml + + terms: + - Butterscotch is regal + - Cara is adorable + +.. + + + See more: :ref:`file-charmcraft-yaml-terms` + + +Add an icon +~~~~~~~~~~~ + + See :ref:`manage-icons`. + + +Add runtime details to a charm +------------------------------ + +Require a specific Juju version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To require a specific Juju version for your charm, in your charm's `charmcraft.yaml` specify the `assumes` key. E.g., + +.. code:: yaml + + assumes: + - juju >= 3.5 + +.. + + + See more: :ref:`file-charmcraft-yaml-assumes` + +Require a Kubernetes cloud +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To require a Kubernetes cloud for your charm, in your charm's `charmcraft.yaml` file specify the `assumes` key. E.g., + +.. code:: yaml + + assumes: + - k8s-api + +.. + + + See more: :ref:`file-charmcraft-yaml-assumes` + +Require a specific base and platforms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To require a specific base and platforms for your charm, in your charm's `charmcraft.yaml` file specify the `base`(,`build-base`,) and the `platforms keys. E.g., + +.. note:: + In Charmcraft < 3.0 this was done via a single key: `bases`. + +.. code:: yaml + + # The run time base, the base format is @, + # accepted bases are: + # - ubuntu@24.04 + base: + # The build time base, if not defined the base is also the build time + # base, in addition to valid bases, the build-base can be "devel" + # which would use the latest in development Ubuntu Series. + build-base: + + platforms: + # The supported platforms, may omit build-for if platform-name + # is a valid arch, valid architectures follow the Debian architecture names, + # accepted architectures are: + # - amd64 + # - arm64 + # - armhf + # - ppc64el + # - riscv64 + # - s390x + : + # The build time architecture + build-on: | + # The run time architecture + build-for: | + +.. + + See more: :ref:`file-charmcraft-yaml-base`, :ref:`build-base`, :ref:`file-charmcraft-yaml-platforms` + +Specify container requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To specify container requirements, in your charm's `charmcraft.yaml` file specify the `containers` key. + + + See more: :ref:`file-charmcraft-yaml-containers` + + +Specify associated resources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To specify the resources associated with the charm, in your charm's `charmcraft.yaml` file specify the `resources` key. + + See :ref:`manage-resources`. + +Specify device requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To specify device requirements, in your charm's `charmcraft.yaml` file specify the `devices` key. + + See more: :ref:`file-charmcraft-yaml-devices` + +Specify storage requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To specify storage requirements, in your charm's `charmcraft.yaml` file specify the `storage` key. + + See more: :ref:`file-charmcraft-yaml-storage` + +Specify extra binding requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To specify extra binding requirements, in your charm's `charmcraft.yaml` file specify the `extra-bindings` key. + + See more: :ref:`file-charmcraft-yaml-extra-bindings` + +Require subordinate deployment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To require subordinate deployment for your charm (i.e., for it to be deployed to the same machine as another charm, called its 'principal'), in your charm's `charmcraft.yaml` file specify the `subordinate` key. + + See more: :ref:`file-charmcraft-yaml-subordinate` + + +Manage actions +~~~~~~~~~~~~~~ + + See first: `Juju \| Action `_, `Juju \| Manage actions `_ + + +To declare an action in your charm, in your charm's `charmcraft.yaml` file specify the `actions` key. + + See more: :ref:`file-charmcraft-yaml-actions` + + See next: `Ops \| Manage actions `_ + + +Manage configurations +~~~~~~~~~~~~~~~~~~~~~ + + See first: `Juju \| Application configuration `_, `Juju \| Manage applications > Configure `_ + +To declare a configuration option for your charm, in your charm's `charmcraft.yaml` specify the `config` key. + + + See more: :ref:`file-charmcraft-yaml-config` + + See next: `Ops \| Manage configurations `_ + + + +Manage relations (integrations) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + See first: `Juju \| Relation `_, `Juju \| Manage relations `_ + + To declare a relation endpoint in your charm, in your charm's `charmcraft.yaml` specify the `peers`, `provides`, or `requires` key. + + See more: :ref:`file-charmcraft-yaml-peers-provides-requires` + + See more: `Ops \| Manage relations (integrations) `_ + +Specify necessary libs +~~~~~~~~~~~~~~~~~~~~~~ + +.. See first: `Juju \| Library <>`_ + + + See more: :ref:`manage-libraries` + +Manage secrets +~~~~~~~~~~~~~~ + + See first: `Juju \| User secret `_ + +To make your charm capable of accepting a user secret, in your charm's `charmcraft.yaml` specify the `config` key with the `type` subkey set to `secret`. + + See more: :ref:`file-charmcraft-yaml-config` + + See next: `Ops \| Manage secrets `_ + +Specify necessary parts +~~~~~~~~~~~~~~~~~~~~~~~ + + See more: :ref:`manage-parts` + +Pack a charm +------------ + +To pack a charm directory, in the charm's root directory, run the command below: + +.. code:: text + + charmcraft pack + + +This will fetch any dependencies (from PyPI, based on `requirements.txt`), compile any modules, check that all the key files are in place, and produce a compressed archive with the extension `.charm`. As you can verify, this archive is just a zip file with metadata and the operator code itself. + +.. dropdown:: Example session for a charm called microsample-vm + + .. code:: text + + # Pack the charm: + ~/microsample-vm$ charmcraft pack + Created 'microsample-vm_ubuntu-22.04-amd64.charm'. + Charms packed: + microsample-vm_ubuntu-22.04-amd64.charm + + # (Optional) Verify that this has created a .charm file in your charm's root directory: + ~/microsample-vm$ ls + CONTRIBUTING.md charmcraft.yaml requirements.txt tox.ini + LICENSE microsample-vm_ubuntu-22.04-amd64.charm src + README.md pyproject.toml tests + + # (Optional) Verify that the .charm file is simply a zip file that contains + # everything you've packed plus any dependencies: + /microsample-vm$ unzip -l microsample-vm_ubuntu-22.04-amd64.charm | { head; tail;} + Archive: microsample-vm_ubuntu-22.04-amd64.charm + Length Date Time Name + --------- ---------- ----- ---- + 815 2023-12-05 12:12 README.md + 11337 2023-12-05 12:12 LICENSE + 250 2023-12-05 12:31 manifest.yaml + 102 2023-12-05 12:31 dispatch + 106 2023-12-01 14:59 config.yaml + 717 2023-12-05 12:31 metadata.yaml + 921 2023-12-05 12:26 src/charm.py + 817 2023-12-01 14:44 venv/setuptools/command/__pycache__/upload.cpython-310.pyc + 65175 2023-12-01 14:44 venv/setuptools/command/__pycache__/easy_install.cpython-310.pyc + 4540 2023-12-01 14:44 venv/setuptools/command/__pycache__/py36compat.cpython-310.pyc + 1593 2023-12-01 14:44 venv/setuptools/command/__pycache__/bdist_rpm.cpython-310.pyc + 6959 2023-12-01 14:44 venv/setuptools/command/__pycache__/sdist.cpython-310.pyc + 2511 2023-12-01 14:44 venv/setuptools/command/__pycache__/rotate.cpython-310.pyc + 2407 2023-12-01 14:44 venv/setuptools/extern/__init__.py + 2939 2023-12-01 14:44 venv/setuptools/extern/__pycache__/__init__.cpython-310.pyc + --------- ------- + 20274163 1538 files + +The command has a number of flags that allow you to specify a different charm directory to pack, whether to force pack if there are linting errors, etc. + + See more: :ref:`ref_commands_pack` + +.. caution:: + **If you’ve declared any resources :** This will *not* pack the resources. This means that, when you upload your charm to Charmhub (if you do), you will have to upload the resources separately. See more: :ref:`manage-resources`. + + + +.. important:: + When the charm is packed, a series of analyses and lintings will happen, you may receive warnings and even errors to help improve the quality of the charm. See more: :ref:`Charmcraft analyzers and linters ` + +.. + + See next: `Juju \| Manage charms `_ + +.. _publish-a-charm: +Publish a charm on Charmhub +--------------------------- + +1. Log in to Charmhub: + +.. code:: text + + charmcraft login + +.. + + See more: :ref:`manage-the-current-charmhub-user` + +2. Register your charm’s name (the one you specified in ``charmcraft.yaml`` > ``name``): + +.. code:: text + + charmcraft register my-awesome-charm + +.. + + See more: :ref:`manage-names` + +.. note:; + This automatically creates 4 channels, all with track latest but with different risk levels, namely, edge, beta, candidate, stable, respectively. See more: :ref:`manage-channels`. + +3. Upload the charm to Charmhub: Use the ``charmcraft upload`` command followed by the your charm’s filepath. E.g., if you are in the charm’s root directory, + +.. code:: text + + charmcraft upload my-awesome-charm.charm + Revision 1 of my-awesome-charm created + +.. + + See more: :ref:`ref_commands_upload` + +.. note:: + Each time you upload a charm to Charmhub, that creates a revision (unless you upload the exact same file again). See more: :ref:`manage-charm-revisions`. + +4. If your charm has associated resources: These are not packed with the + rest of the charm project, so you must upload them explicitly to + Charmhub as well. For example: + +.. code:: text + + $ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin + Revision 1 created of resource 'someresource' for charm 'my-super-charm' + +.. + + See more: :ref:`manage-resources` + +.. note:: + Each time you upload a resource to Charmhub, that creates a revision (unless you upload the exact same file again). See more: :ref:`manage-resource-revisions`. + +5. Release the charm: To release a charm, release your revision of + choice to the target release channel. E.g., + +.. code:: text + + $ charmcraft release my-awesome-charm --revision=1 --channel=beta + Revision 1 of charm 'my-awesome-charm' released to beta + +.. + + See more: :ref:`manage-charm-revisions` + +.. note:: + This automatically opens the channel. See more: :ref:`manage-channels`. + +.. + + See next: `Juju \| Deploy a Charmub charm `_, `Juju \| Update a Charmhub charm `_ + +.. tip:: + To update the charm on Charmhub, repeat the upload and release steps. + +.. important:: + Releasing a charm on Charmhub gives it a public URL. However, the charm will not appear in the Charmhub search results until it has passed formal review. To request formal review, reach out to the community to announce your charm and ask for a review by an experienced community member. See more: `Discourse \| review requests `_. + + Also, the point of publishing and having a charm publicly listed on Charmhub is so others can reuse it and potentially contribute to it as well. To publicize your charm: + + - `Write a Discourse post to announce your + release. `_ + + - `Schedule a community workshop to demo your charm’s + capabilities. `_ + + - `Chat about it with your charmer + friends. `_ + +.. diff --git a/docs/howto/manage-extensions.md b/docs/howto/manage-extensions.md deleted file mode 100644 index ae16f8fa9..000000000 --- a/docs/howto/manage-extensions.md +++ /dev/null @@ -1,209 +0,0 @@ -(manage-extensions)= -# How to manage extensions - -> See also: {ref}`extension` - -## View all the available extensions - -To view all the available Rockcraft / Charmcraft extensions, run the `rockcraft list-extensions` / `charmcraft list-extensions` command. Sample session: - -```bash -$ charmcraft list-extensions -Extension name Supported bases Experimental bases ----------------- ----------------- -------------------- -flask-framework ubuntu@22.04 -``` - -> See more: [Rockcraft | `rockcraft list-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/list-extensions/), {ref}`ref_commands_list-extensions` - -## View details about the extension in use - -Suppose you've initialised a rock / charm with a profile that comes with an extension (currently, `flask-framework`), and your `rockcraft.yaml` / `charmcraft.yaml > extensions` lists this extension. - - -````{dropdown} See example context - -```bash -$ mkdir my-flask-app-k8s -$ cd my-flask-app-k8s/ -$ charmcraft init --profile flask-framework -Charmed operator package file and directory tree initialised. - -Now edit the following package files to provide fundamental charm metadata -and other information: - -charmcraft.yaml -src/charm.py -README.md - -user@ubuntu:~/my-flask-app-k8s$ ls -R -.: -charmcraft.yaml requirements.txt src - -./src: -charm.py - -$ cat charmcraft.yaml -# This file configures Charmcraft. -# See https://juju.is/docs/sdk/charmcraft-config for guidance. - -name: my-flask-app-k8s - -type: charm - -bases: - - build-on: - - name: ubuntu - channel: "22.04" - run-on: - - name: ubuntu - channel: "22.04" - -# (Required) -summary: A very short one-line summary of the flask application. - -# (Required) -description: | - A comprehensive overview of your Flask application. - -extensions: - - flask-framework - -# Uncomment the integrations used by your application -# requires: -# mysql: -# interface: mysql_client -# limit: 1 -# postgresql: -# interface: postgresql_client -# limit: 1 - - -``` - -```` - -To view details about what that extension is adding to your charm, set the `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS` to `1`, then run `charmcraft expand-extensions`. For example: - - -```text -CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions -``` - - -````{dropdown} See effect given example context - -``` -$ CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions -*EXPERIMENTAL* extension 'flask-framework' enabled -name: my-flask-app-k8s -summary: A very short one-line summary of the flask application. -description: | - A comprehensive overview of your Flask application. -parts: - charm: - source: . - charm-entrypoint: src/charm.py - charm-binary-python-packages: [] - charm-python-packages: [] - charm-requirements: - - requirements.txt - charm-strict-dependencies: false - plugin: charm -type: charm -bases: -- build-on: - - name: ubuntu - channel: '22.04' - run-on: - - name: ubuntu - channel: '22.04' -actions: - rotate-secret-key: - description: Rotate the flask secret key. Users will be forced to log in again. - This might be useful if a security breach occurs. -assumes: -- k8s-api -containers: - flask-app: - resource: flask-app-image -peers: - secret-storage: - interface: secret-storage -provides: - metrics-endpoint: - interface: prometheus_scrape - grafana-dashboard: - interface: grafana_dashboard -requires: - logging: - interface: loki_push_api - ingress: - interface: ingress - limit: 1 -resources: - flask-app-image: - type: oci-image - description: flask application image. -config: - options: - webserver-keepalive: - type: int - description: Time in seconds for webserver to wait for requests on a Keep-Alive - connection. - webserver-threads: - type: int - description: Run each webserver worker with the specified number of threads. - webserver-timeout: - type: int - description: Time in seconds to kill and restart silent webserver workers. - webserver-workers: - type: int - description: The number of webserver worker processes for handling requests. - flask-application-root: - type: string - description: Path in which the application / web server is mounted. This configuration - will set the FLASK_APPLICATION_ROOT environment variable. Run `app.config.from_prefixed_env()` - in your Flask application in order to receive this configuration. - flask-debug: - type: boolean - description: Whether Flask debug mode is enabled. - flask-env: - type: string - description: What environment the Flask app is running in, by default it's 'production'. - flask-permanent-session-lifetime: - type: int - description: Time in seconds for the cookie to expire in the Flask application - permanent sessions. This configuration will set the FLASK_PERMANENT_SESSION_LIFETIME - environment variable. Run `app.config.from_prefixed_env()` in your Flask application - in order to receive this configuration. - flask-preferred-url-scheme: - type: string - default: HTTPS - description: Scheme for generating external URLs when not in a request context - in the Flask application. By default, it's "HTTPS". This configuration will - set the FLASK_PREFERRED_URL_SCHEME environment variable. Run `app.config.from_prefixed_env()` - in your Flask application in order to receive this configuration. - flask-secret-key: - type: string - description: The secret key used for securely signing the session cookie and - for any other security related needs by your Flask application. This configuration - will set the FLASK_SECRET_KEY environment variable. Run `app.config.from_prefixed_env()` - in your Flask application in order to receive this configuration. - flask-session-cookie-secure: - type: boolean - description: Set the secure attribute in the Flask application cookies. This - configuration will set the FLASK_SESSION_COOKIE_SECURE environment variable. - Run `app.config.from_prefixed_env()` in your Flask application in order to - receive this configuration. - -``` - -```` - - -> See more: [`rockcraft expand-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/expand-extensions/), {ref}`ref_commands_expand-extensions` - -
- -> **Contributors:** @lengau, @tmihoc diff --git a/docs/howto/manage-extensions.rst b/docs/howto/manage-extensions.rst new file mode 100644 index 000000000..2b13428e0 --- /dev/null +++ b/docs/howto/manage-extensions.rst @@ -0,0 +1,208 @@ +.. _manage-extensions: +How to manage extensions +======================== + + See also: :ref:`extension` + +View all the available extensions +--------------------------------- + +To view all the available Rockcraft / Charmcraft extensions, run the ``rockcraft list-extensions`` / ``charmcraft list-extensions`` command. For example: + +.. code:: text + + $ charmcraft list-extensions + Extension name Supported bases Experimental bases + ---------------- ----------------- -------------------- + flask-framework ubuntu@22.04 + +.. + + See more: `Rockcraft \| rockcraft list-extensions `_, :ref:`ref_commands_list-extensions` + +View details about the extension in use +--------------------------------------- + +Suppose you’ve initialised a rock / charm with a profile that comes with +an extension (currently, ``flask-framework``), and your +``rockcraft.yaml`` / ``charmcraft.yaml > extensions`` lists this +extension. + +.. dropdown:: Example + + .. code:: text + + $ mkdir my-flask-app-k8s + $ cd my-flask-app-k8s/ + $ charmcraft init --profile flask-framework + Charmed operator package file and directory tree initialised. + + Now edit the following package files to provide fundamental charm metadata + and other information: + + charmcraft.yaml + src/charm.py + README.md + + user@ubuntu:~/my-flask-app-k8s$ ls -R + .: + charmcraft.yaml requirements.txt src + + ./src: + charm.py + + $ cat charmcraft.yaml + # This file configures Charmcraft. + # See https://juju.is/docs/sdk/charmcraft-config for guidance. + + name: my-flask-app-k8s + + type: charm + + bases: + - build-on: + - name: ubuntu + channel: "22.04" + run-on: + - name: ubuntu + channel: "22.04" + + # (Required) + summary: A very short one-line summary of the flask application. + + # (Required) + description: | + A comprehensive overview of your Flask application. + + extensions: + - flask-framework + + # Uncomment the integrations used by your application + # requires: + # mysql: + # interface: mysql_client + # limit: 1 + # postgresql: + # interface: postgresql_client + # limit: 1 + + +To view details about what that extension is adding to your charm, set the ``CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS`` to ``1``, then run ``charmcraft expand-extensions``. For example: + + +.. code:: text + + CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions + + +.. dropdown:: See effect given example context + + + .. code:: text + + $ CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions + *EXPERIMENTAL* extension 'flask-framework' enabled + name: my-flask-app-k8s + summary: A very short one-line summary of the flask application. + description: | + A comprehensive overview of your Flask application. + parts: + charm: + source: . + charm-entrypoint: src/charm.py + charm-binary-python-packages: [] + charm-python-packages: [] + charm-requirements: + - requirements.txt + charm-strict-dependencies: false + plugin: charm + type: charm + bases: + - build-on: + - name: ubuntu + channel: '22.04' + run-on: + - name: ubuntu + channel: '22.04' + actions: + rotate-secret-key: + description: Rotate the flask secret key. Users will be forced to log in again. + This might be useful if a security breach occurs. + assumes: + - k8s-api + containers: + flask-app: + resource: flask-app-image + peers: + secret-storage: + interface: secret-storage + provides: + metrics-endpoint: + interface: prometheus_scrape + grafana-dashboard: + interface: grafana_dashboard + requires: + logging: + interface: loki_push_api + ingress: + interface: ingress + limit: 1 + resources: + flask-app-image: + type: oci-image + description: flask application image. + config: + options: + webserver-keepalive: + type: int + description: Time in seconds for webserver to wait for requests on a Keep-Alive + connection. + webserver-threads: + type: int + description: Run each webserver worker with the specified number of threads. + webserver-timeout: + type: int + description: Time in seconds to kill and restart silent webserver workers. + webserver-workers: + type: int + description: The number of webserver worker processes for handling requests. + flask-application-root: + type: string + description: Path in which the application / web server is mounted. This configuration + will set the FLASK_APPLICATION_ROOT environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-debug: + type: boolean + description: Whether Flask debug mode is enabled. + flask-env: + type: string + description: What environment the Flask app is running in, by default it's 'production'. + flask-permanent-session-lifetime: + type: int + description: Time in seconds for the cookie to expire in the Flask application + permanent sessions. This configuration will set the FLASK_PERMANENT_SESSION_LIFETIME + environment variable. Run `app.config.from_prefixed_env()` in your Flask application + in order to receive this configuration. + flask-preferred-url-scheme: + type: string + default: HTTPS + description: Scheme for generating external URLs when not in a request context + in the Flask application. By default, it's "HTTPS". This configuration will + set the FLASK_PREFERRED_URL_SCHEME environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-secret-key: + type: string + description: The secret key used for securely signing the session cookie and + for any other security related needs by your Flask application. This configuration + will set the FLASK_SECRET_KEY environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-session-cookie-secure: + type: boolean + description: Set the secure attribute in the Flask application cookies. This + configuration will set the FLASK_SESSION_COOKIE_SECURE environment variable. + Run `app.config.from_prefixed_env()` in your Flask application in order to + receive this configuration. + +.. + + See more: `Rockcraft \| rockcraft expand-extensions `_, :ref:`ref_commands_expand-extensions` diff --git a/docs/howto/manage-icons.md b/docs/howto/manage-icons.md deleted file mode 100644 index c03cfd0da..000000000 --- a/docs/howto/manage-icons.md +++ /dev/null @@ -1,70 +0,0 @@ -(manage-icons)= -# How to manage icons - - -## Learn about icon requirements and best practices - -See {ref}`file-icon-svg`. - - -## Create an icon - -Before you start you will need: - -- A vector graphic editor. We strongly recommend the cross-platform and most excellent [Inkscape](http://www.inkscape.org) for all your vector graphic needs. -- [The template file.](https://assets.ubuntu.com/v1/fc0260eb-icon.svg) (right-click > Save link as...) -- An existing logo you can import, or the ability to draw one in Inkscape. - -Once you have those, fire up Inkscape and we can begin! - -### 1. Open the template - - -From Inkscape load the `icon.svg` file. Select the Layer called "Background Circle", either from the drop down at the bottom, or from the layer dialog. - -![Open the template](manage-icons-create-1.png) - -### 3. Add colour - -In the menu, select **Object** and then **Fill and Stroke** to adjust the colour. - -![Add color](manage-icons-create-2.png) - - -### 2. Draw something - - -Draw your shape within the circle. If you already have a vector logo, you can import it and scale it within the guides. Inkscape also has plenty of drawing tools for creating complex images. - -If you import a bitmap image to use, be sure to convert it into a vector file and delete the bitmap. - -![Draw something](manage-icons-create-3.png) - -*Cloud icon: "Cloud by unlimicon from the Noun Project" [CC BY]* - - - -## Validate an icon - -You can validate your icon at [charmhub.io/icon-validator](https://charmhub.io/icon-validator). The page checks the most basic issues that prevent icons working. - -![Validate](manage-icons-validate.png) - - -```{note} - - -``` - -## Add an icon to its charm's Charmhub page - -To add the icon to the charm's Charmhub page, save it as `icon.svg`, place it in the root directory of the charm, and then publish the charm to a channel of the form `/stable` (e.g., `latest/stable`). - -```{note} - -That is because Charmhub only updates the metadata for a charm on stable channel releases [(by design)](https://snapcraft.io/blog/better-snap-metadata-handling-coming-your-way-soon). -So either release the revision with the icon to a `stable` channel and then roll it back, or wait until your charm is ready for a "stable" `stable` release. - -``` - -> See more: {ref}`publish-a-charm` diff --git a/docs/howto/manage-icons.rst b/docs/howto/manage-icons.rst new file mode 100644 index 000000000..c9777a185 --- /dev/null +++ b/docs/howto/manage-icons.rst @@ -0,0 +1,83 @@ +.. _manage-icons: +How to manage icons +=================== + +Learn about icon requirements and best practices +------------------------------------------------ + + See more: :ref:`file-icon-svg` + +Create an icon +-------------- + +Before you start you will need: + +- A vector graphic editor. We strongly recommend the cross-platform and + most excellent `Inkscape `__ for all your + vector graphic needs. +- `The template + file. `__ + (right-click > Save link as…) +- An existing logo you can import, or the ability to draw one in + Inkscape. + +Once you have those, fire up Inkscape and we can begin! + +1. Open the template +~~~~~~~~~~~~~~~~~~~~ + +From Inkscape load the ``icon.svg`` file. Select the Layer called +“Background Circle”, either from the drop down at the bottom, or from +the layer dialog. + +.. figure:: manage-icons-create-1.png + :alt: Open the template + + Open the template + +3. Add colour +~~~~~~~~~~~~~ + +In the menu, select **Object** and then **Fill and Stroke** to adjust +the colour. + +.. figure:: manage-icons-create-2.png + :alt: Add color + + +2. Draw something +~~~~~~~~~~~~~~~~~ + +Draw your shape within the circle. If you already have a vector logo, +you can import it and scale it within the guides. Inkscape also has +plenty of drawing tools for creating complex images. + +If you import a bitmap image to use, be sure to convert it into a vector +file and delete the bitmap. + +.. figure:: manage-icons-create-3.png + :alt: Draw something + +*Cloud icon: “Cloud by unlimicon from the Noun Project” [CC BY]* + +Validate an icon +---------------- + +You can validate your icon at +`charmhub.io/icon-validator `_. The +page checks the most basic issues that prevent icons working. + +.. figure:: manage-icons-validate.png + :alt: Validate + +Add an icon to its charm's Charmhub page +---------------------------------------- + +To add the icon to the charm's Charmhub page, save it as ``icon.svg``, place it in the root directory of the charm, and then publish the charm to a channel of the form ``/stable`` (e.g., ``latest/stable``). + +.. note:: + That is because Charmhub only updates the metadata for a charm on stable channel releases (`by design `_). So either release the revision with the icon to a ``stable`` channel and then roll it back, or wait until your charm is ready for a "stable" ``stable`` release. + +.. + + See more: :ref:`publish-a-charm` diff --git a/docs/howto/manage-libraries.md b/docs/howto/manage-libraries.md deleted file mode 100644 index edfe1735c..000000000 --- a/docs/howto/manage-libraries.md +++ /dev/null @@ -1,111 +0,0 @@ -(manage-libraries)= -# How to manage libraries -> See first: [`juju` | Library]() - - -## Initialise a library - -> See also: {ref}``charmcraft create-lib` ` - -In your charm's root directory, run `charmcraft create-lib`: - -```text -# Initialise a charm library named 'demo' -$ charmcraft create-lib demo -``` - -```{note} - -Before creating a library, you must first register ownership of your charm’s name. See more: {ref}`publish-a-charm`. - -``` - -This will create a template file at `$CHARMDIR/lib/charms/demo/v0/demo.py`. - - -> See more: {ref}`ref_commands_create-lib`, {ref}`file-libname-py` - -Edit this file to write your library. - -```{important} - -A library must comprise a single Python file. If you write a library that feels too "big" for a single file, it is likely that the library should be split up, or that you are actually writing a full-on charm. - -``` - -> See next: [`ops` | Manage libraries]() - - -(publish-a-library)= -## Publish a library on Charmhub - - -```{caution} - -On Charmhub, a library is always associated with the charm that it was first created for. When you publisht it to Charmhub, it's published to the page of that charm. To be able to publish it, you need to be logged in to Charmhub as a user who owns the charm (see more: {ref}`publish-a-charm`) or as a user who is registered as a contributor to the charm (a status that can be requested via [Discourse](https://discourse.charmhub.io/). - -``` - -To publish a library on Charmhub, in the root directory of the charm that holds the library, run `charmcraft publish-lib` followed by the full library path on the template `charms..v.`. For example: - -```text -$ charmcraft publish-lib charms.demo.v0.demo -Library charms.demo.v0.demo sent to the store with version 0.1 -``` - -> See more: {ref}`ref_commands_publish-lib` - -This will upload the library's content to Charmhub. - -To update the library on Charmhub, update the `LIBAPI`/`LIBPATCH` metadata fields inside the library file, then repeat the publish procedure. - -```{caution} **About the metadata fields:** - -Most times it is enough to just increment `LIBPATCH` but, if you're introducing breaking changes, you must work with the major API version. - -Additionally, be mindful of the fact that users of your library will update it automatically to the latest PATCH version with the same API version. To avoid breaking other people's library usage, make sure to increment the `LIBAPI` version but reset `LIBPATCH` to `0`. Also, before adding the breaking changes and updating these values, make sure to copy the library to the new path; this way you can maintain different major API versions independently, being able to update, for example, your v0 after publishing v1. See more: {ref}`file-libname-py`. -``` - -> See more: {ref}`ref_commands_publish-lib` - - -To share your library with other charm developers, navigate to the host charm's Charmhub page, go to Libraries tab, then copy and share the URL at the top of the page. - - -## View the libs published for a charm - -The easiest way to find an existing library for a given charm is via `charmcraft list-lib`, as shown below. This will query Charmhub and show which libraries are published for the specified charm, along with API/patch versions. - - jdoe@machine:/home/jane/autoblog$ charmcraft list-lib blogsystem - Library name API Patch - superlib 1 0 - -The listing will not show older API versions; this ensures that new users always start with the latest version. - -Another good way to search for libraries is to explore the charm collection on [Charmhub](https://charmhub.io/). - -> See more: {ref}``charmcraft list-lib` ` - - -## Use a library - -In your charm's `charmcraft.yaml`, specify the `charm-libs` key with the desired libraries. - -> See more: {ref}`file-charmcraft-yaml-charm-libs` - - -In your charm's root directory, run `charmcraft fetch-libs`. Charmcraft will download the libraries to your charm's directory. - -> See more: {ref}`ref_commands_fetch-libs` - - -To use a library in your `src/charm.py`, import it using its fully-qualified path minus the `lib` part: - -```python -import charms.demo.v0.demo -``` - -To update your lib with the latest published version, repeat the process. - - - diff --git a/docs/howto/manage-libraries.rst b/docs/howto/manage-libraries.rst new file mode 100644 index 000000000..6cccd2ad7 --- /dev/null +++ b/docs/howto/manage-libraries.rst @@ -0,0 +1,111 @@ +.. _manage-libraries: +How to manage libraries +======================= + + +.. See first: `Juju \| Library <>`_ + +Initialise a library +-------------------- + + See also: :ref:`ref_commands_create-lib` + +In your charm’s root directory, run ``charmcraft create-lib``: + +.. code:: text + + # Initialise a charm library named 'demo' + $ charmcraft create-lib demo + +.. note:: + Before creating a library, you must first register ownership of your charm’s name. See more: :ref:`publish-a-charm`. + + +This will create a template file at ``$CHARMDIR/lib/charms/demo/v0/demo.py``. + + See more: :ref:`ref_commands_create-lib`, :ref:`file-libname-py` + +Edit this file to write your library. + +.. important:: + A library must comprise a single Python file. If you write a library that feels too "big" for a single file, it is likely that the library should be split up, or that you are actually writing a full-on charm. + +.. + + See next: `Ops \| Manage libraries `_ + +.. _publish-a-library: +Publish a library on Charmhub +----------------------------- + +.. caution:: + On Charmhub, a library is always associated with the charm that it was first created for. When you publisht it to Charmhub, it’s published to the page of that charm. To be able to publish it, you need to be logged in to Charmhub as a user who owns the charm (see more: :ref:`publish-a-charm`) or as a user who is registered as a contributor to the charm (a status that can be requested via `Discourse `_). + + +To publish a library on Charmhub, in the root directory of the charm that holds the library, run `charmcraft publish-lib` followed by the full library path on the template `charms..v.`. For example: + +.. code:: text + + $ charmcraft publish-lib charms.demo.v0.demo + Library charms.demo.v0.demo sent to the store with version 0.1 + +.. + + See more: :ref:`ref_commands_publish-lib` + +This will upload the library’s content to Charmhub. + +To update the library on Charmhub, update the ``LIBAPI``/``LIBPATCH`` metadata fields inside the library file, then repeat the publish procedure. + + + See more: :ref:`ref_commands_publish-lib` + + +.. caution:: **About the metadata fields:** + Most times it is enough to just increment ``LIBPATCH`` but, if you’re introducing breaking changes, you must work with the major API version. + Additionally, be mindful of the fact that users of your library will update it automatically to the latest PATCH version with the same API version. To avoid breaking other people’s library usage, make sure toincrement the ``LIBAPI`` version but reset ``LIBPATCH`` to ``0``. Also, before adding the breaking changes and updating these values, make sureto copy the library to the new path; this way you can maintain different major API versions independently, being able to update, for example, your v0 after publishing v1. See more: :ref:`file-libname-py`. + +.. + + +To share your library with other charm developers, navigate to the host charm's Charmhub page, go to Libraries tab, then copy and share the URL at the top of the page. + + +View the libs published for a charm +----------------------------------- + +The easiest way to find an existing library for a given charm is via `charmcraft list-lib`, as shown below. This will query Charmhub and show which libraries are published for the specified charm, along with API/patch versions. + +.. code:: + + jdoe@machine:/home/jane/autoblog$ charmcraft list-lib blogsystem + Library name API Patch + superlib 1 0 + +The listing will not show older API versions; this ensures that new users always start with the latest version. + +Another good way to search for libraries is to explore the charm collection on [Charmhub](https://charmhub.io/). + + See more: :ref:`ref_commands_list-lib>` + + +Use a library +------------- + +In your charm's `charmcraft.yaml`, specify the `charm-libs` key with the desired libraries. + + See more: :ref:`file-charmcraft-yaml-charm-libs` + + +In your charm's root directory, run `charmcraft fetch-libs`. Charmcraft will download the libraries to your charm's directory. + + See more: :ref:`ref_commands_fetch-libs` + + +To use a library in your `src/charm.py`, import it using its fully-qualified path minus the `lib` part: + +.. code:: python + + import charms.demo.v0.demo + +To update your lib with the latest published version, repeat the process. diff --git a/docs/howto/manage-names.md b/docs/howto/manage-names.md deleted file mode 100644 index 916e17dc3..000000000 --- a/docs/howto/manage-names.md +++ /dev/null @@ -1,36 +0,0 @@ -(manage-names)= -# How to manage names - - -(register-a-name)= -## Register a name on Charmhub - - -To register a name for your charm on Charmhub, use the `chamrcraft register` command followed by your desired name. E.g., - -```bash -$ charmcraft register my-awesome-charm -Congrats! You are now the publisher of 'my-awesome-charm' -``` - -> See more: {ref}`ref_commands_register` - -This also automatically creates four channels, all with track `latest` but risk level `edge`, `beta`, `candidate`, and `stable`, respectively. - -> See more: {ref}`manage-channels` - -## View registered names - -To view the names you've registered on Charmhub, run `charmcraft names`. - -> See more: {ref}`ref_commands_names` - -## Unregister a name - -```{caution} -A name can be unregistered only if you haven't yet uploaded anything to it. -``` - -To unregister a name, run `charmcraft unregister` followed by the name. - -> See more: {ref}`ref_commands_unregister` diff --git a/docs/howto/manage-names.rst b/docs/howto/manage-names.rst new file mode 100644 index 000000000..89b4cb7bc --- /dev/null +++ b/docs/howto/manage-names.rst @@ -0,0 +1,43 @@ +.. _manage-names: +How to manage names +=================== + +.. _register-a-name: +Register a name on Charmhub +--------------------------- + +To register a name for your charm on Charmhub, use the ``chamrcraft register`` command followed by your desired name. E.g., + +.. code:: text + + $ charmcraft register my-awesome-charm + Congrats! You are now the publisher of 'my-awesome-charm' + +.. + + See more: :ref:`ref_commands_register` + +This also automatically creates four channels, all with track ``latest`` +but risk level ``edge``, ``beta``, ``candidate``, and ``stable``, +respectively. + + See more: :ref:`manage-channels` + +View registered names +--------------------- + +To view the names you’ve registered on Charmhub, run +``charmcraft names``. + + See more: :ref:`ref_commands_names` + +Unregister a name +----------------- + +.. caution:: + A name can be unregistered only if you haven't yet uploaded anything to it. + +To unregister a name, run ``charmcraft unregister`` followed by the +name. + + See more: :ref:`ref_commands_unregister` diff --git a/docs/howto/manage-parts.md b/docs/howto/manage-parts.md deleted file mode 100644 index f9da9c76b..000000000 --- a/docs/howto/manage-parts.md +++ /dev/null @@ -1,48 +0,0 @@ -(manage-parts)= -# How to manage parts - -> See first: {ref}`part` - - -## Remove a part's assets - -TBA - -> See more: {ref}`ref_commands_clean` - - -## Download or retrieve artifacts defined for a part - -TBA - -> See more: {ref}`ref_commands_pull` - - -## Build artifacts defined for a part - -TBA - -> See more: {ref}`ref_commands_build` - - -## Stage built artifacts into a common staging area - -TBA - -> See more: {ref}`ref_commands_build` - - -## Prime artifacts defined for a part - -TBA - -> See more: {ref}`ref_commands_prime` - - -## Build the charm or bundle - - -TBA - -> See more: {ref}`ref_commands_pack` - diff --git a/docs/howto/manage-parts.rst b/docs/howto/manage-parts.rst new file mode 100644 index 000000000..bf4e92ece --- /dev/null +++ b/docs/howto/manage-parts.rst @@ -0,0 +1,47 @@ +.. _manage-parts: +How to manage parts +=================== + + See first: :ref:`part` + +Remove a part’s assets +---------------------- + +TBA + + See more: :ref:`ref_commands_clean` + +Download or retrieve artifacts defined for a part +------------------------------------------------- + +TBA + + See more: :ref:`ref_commands_pull` + +Build artifacts defined for a part +---------------------------------- + +TBA + + See more: :ref:`ref_commands_build` + +Stage built artifacts into a common staging area +------------------------------------------------ + +TBA + + See more: :ref:`ref_commands_build` + +Prime artifacts defined for a part +---------------------------------- + +TBA + + See more: :ref:`ref_commands_prime` + +Build the charm or bundle +------------------------- + +TBA + + See more: :ref:`ref_commands_pack` diff --git a/docs/howto/manage-resources.md b/docs/howto/manage-resources.md deleted file mode 100644 index b6edd18b8..000000000 --- a/docs/howto/manage-resources.md +++ /dev/null @@ -1,97 +0,0 @@ -manage-resources)= -# How to manage resources - -> See first: [`juju` | Charm resource](https://juju.is/docs/juju/charm-resource), [`juju` | Manage resources](https://juju.is/docs/juju/manage-charm-resources) - -## Declare a resource - -To declare a resource required by your charm, in your charm's `charmcraft.yaml file` specify the `resources` key. - -> See more: {ref}`file-charmcraft-yaml-resources` -> -> See next: [`ops` | Manage resources]() - - -````{tip} -During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. For example, assuming the resource is a `/tmp/somefile.txt` file, you could pack and the deploy with `juju deploy ... --resource`: - -```text -echo "TEST" > /tmp/somefile.txt -charmcraft pack -juju deploy ./my-charm.charm --resource my-resource=/tmp/somefile.txt -``` - -```` - -(publish-a-resource)= -## Publish a resource on Charmhub - -```{note} -You must have already published the charm. See more: {ref}`publish-a-charm`. - -``` - -To publish a resource on its charm's Charmhub page, run the `charmcraft upload-resource` command followed by the name of the charm, the name of the resource (cf. `charmcraft.yaml`), and `--filepath=` / `--image=`. For example: - -```{note} - -The option `--image` must indicate an OCI image's digest, being it in the short or long form (e.g.: `70aa8983ec5c` or `sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249`). When using the "short form" of the digest, the image needs to be present locally so its proper ID (the "long form") can be retrieved. - - -``` - - -```text -$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin -Revision 1 created of resource 'someresource' for charm 'my-super-charm' -``` - -```text -$ charmcraft upload-resource my-super-charm redis-image --image=sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249 -Revision 1 created of resource 'redis-image' for charm 'my-super-charm' -``` - -Charmcraft will first check if that specific image is available in Canonical's Registry, and just use it if that's the case. If not, it will try to get it from the developer's local OCI repository (needs `dockerd` to be installed and running), push it to the Canonical's Registry, and then use it. Either way, when the upload has completed, you end up with a resource revision. - -To update a pre-uploaded resource, run the `upload-resource` command again. The result will be a new revision. - -> See more: {ref}`ref_commands_upload-resource` - -## View all the resources published on Charmhub - -To view all the resources published on Charmhub for a charm, run `charmcraft resources` followed by the charm name: - -```{important} - -**If you're not logged in to Charmhub:** The command will open up a browser window and ask you to log in. - -``` - -```text -$ charmcraft resources mycharm -``` - -> See more: {ref}`ref_commands_resources` - -(manage-resource-revisions)= -## Manage resource revisions -### List all the available resource revisions - -To view all the revisions for a resource associated with a charm you've uploaded to Charmhub, run `charmcraft resource-revisions` followed by the charm name and the resource name. For example: - -```text -$ charmcraft resource-revisions mycharm myresource -``` - -> See more: {ref}`ref_commands_resource-revisions` - -### Set the architectures for a resource revision - -To set the architectures for a revision of a resource associated with a charm you've uploaded to Charmhub, run `charmcraft set-resource-architectures` followed by the name of the charm, the name of the resource, and the architecture(s), using the `--resources` flag to specify the target resource revision. For example: - -```text -$ charmcraft set-resource-architectures mycharm myresource --revision=1 arm64,armhf -``` - -> See more: {ref}`ref_commands_set-resource-architectures` - diff --git a/docs/howto/manage-resources.rst b/docs/howto/manage-resources.rst new file mode 100644 index 000000000..6b0b30e8f --- /dev/null +++ b/docs/howto/manage-resources.rst @@ -0,0 +1,106 @@ +.. _manage-resources: +How to manage resources +======================= + + See first: `Juju \| Charm resource `_, `Juju \| + Manage resources `_ + +Declare a resource +------------------ + +To declare a resource required by your charm, in your charm’s ``charmcraft.yaml file`` specify the ``resources`` key. + + See more: :ref:`file-charmcraft-yaml-resources` + + See next: `Ops \| Manage resources `_ + +.. tip:: + During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. For example, assuming the resource is a ``/tmp/somefile.txt`` file, you could pack and the deploy with ``juju deploy … –resource``: + +.. code:: text + + echo "TEST" > /tmp/somefile.txt + charmcraft pack + juju deploy ./my-charm.charm --resource my-resource=/tmp/somefile.txt + + +.. _publish-a-resource: +Publish a resource on Charmhub +------------------------------ + +.. note: You must have already published the charm. See more: :ref:`publish-a-charm`. + +To publish a resource on its charm's Charmhub page, run the ``charmcraft upload-resource`` command followed by the name of the charm, the name of the resource (cf. ``charmcraft.yaml``), and ``--filepath=`` / ``--image=``. For example: + +.. note:: + The option ``--image`` must indicate an OCI image's digest, being it in the short or long form (e.g.: ``70aa8983ec5c`` or ``sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249``). When using the "short form" of the digest, the image needs to be present locally so its proper ID (the "long form") can be retrieved. + +.. code:: text + + $ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin + Revision 1 created of resource 'someresource' for charm 'my-super-charm' + +.. code:: text + + $ charmcraft upload-resource my-super-charm redis-image --image=sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249 + Revision 1 created of resource 'redis-image' for charm 'my-super-charm' + +Charmcraft will first check if that specific image is available in Canonical’s Registry, and just use it if that’s the case. If not, it will try to get it from the developer’s local OCI repository (needs ``dockerd`` to be installed and running), push it to the Canonical’s Registry, and then use it. Either way, when the upload has completed, you end up with a resource revision. + +To update a pre-uploaded resource, run the ``upload-resource`` command again. The result will be a new revision. + + See more: :ref:`ref_commands_upload-resource` + +View all the resources published on Charmhub +-------------------------------------------- + +To view all the resources published on Charmhub for a charm, run +``charmcraft resources`` followed by the charm name: + +.. important:: **If you’re not logged in to Charmhub:** + The command will open up a browser window and ask you to log in. + +.. code:: text + + $ charmcraft resources mycharm + +.. + + See more: :ref:`ref_commands_resources` + +.. _manage-resource-revisions: +Manage resource revisions +------------------------- + +List all the available resource revisions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To view all the revisions for a resource associated with a charm you’ve +uploaded to Charmhub, run ``charmcraft resource-revisions`` followed by +the charm name and the resource name. For example: + +.. code:: text + + $ charmcraft resource-revisions mycharm myresource + +.. + + See more: :ref:`ref_commands_resource-revisions` + +Set the architectures for a resource revision +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To set the architectures for a revision of a resource associated with a +charm you’ve uploaded to Charmhub, run +``charmcraft set-resource-architectures`` followed by the name of the +charm, the name of the resource, and the architecture(s), using the +``--resources`` flag to specify the target resource revision. For +example: + +.. code:: text + + $ charmcraft set-resource-architectures mycharm myresource --revision=1 arm64,armhf + +.. + + See more: :ref:`ref_commands_set-resource-architectures` diff --git a/docs/howto/manage-revisions.md b/docs/howto/manage-revisions.md deleted file mode 100644 index 83bcecc9a..000000000 --- a/docs/howto/manage-revisions.md +++ /dev/null @@ -1,81 +0,0 @@ -(manage-charm-revisions)= -# How to manage charm revisions - - - -## Create a charm revision - -A charm revision is created implicitly every time you upload a charm to Charmhub (unless you're uploading the exact same file again). - -> See more: {ref}`publish-a-charm` - -## View the existing charm revisions - - - To inspect the existing charm revisions, run `charmcraft revisions` followed by the name of the charm. - -> See more: {ref}`ref_commands_revisions` - - -## Promote a charm revision to a better risk level - -To promote a charm revision to a higher-ranking risk level, - - - -use the GitHub `promote-charm` action. - -> See more: [GitHub | canonical/charming-actions/promote-charm](https://github.com/canonical/charming-actions/tree/2.6.0/promote-charm) - -````{dropdown} Example outcome - -For example, in the (partial) output of juju info mongodb below, revision 100 has been promoted from `3.6/edge` through `3.6/beta` and `3.6/candidate` all the way to `3.6/stable`. (The up arrow next to `3.6/beta` indicates that that channel has been closed and, if you try `juju deploy --channel 3.6/beta`, what you’ll get is the next higher-ranking risk level of the same track, that is, `3.6/candidate`.) - -```text -channels: | - 5/stable: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 - 5/candidate: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 - 5/beta: ↑ - 5/edge: 118 2023-05-03 (118) 13MB amd64 ubuntu@22.04 - 3.6/stable: 100 2023-04-28 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 - 3.6/candidate: 100 2023-04-13 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 - 3.6/beta: ↑ - 3.6/edge: 100 2023-02-03 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 - -``` -```` - - -(release-a-revision-into-a-channel)= -## Release a charm revision into a channel - -To release a specific charm revision to a channel, run `charmcraft release` followed by the name of the charm and flags specifying the revision and its target channel. E.g., - -```text -$ charmcraft release my-awesome-charm --revision=1 --channel=beta -Revision 1 of charm 'my-awesome-charm' released to beta -``` - -> See more: {ref}`ref_commands_release` - - -This opens the channel you're releasing to. - - -> See more: {ref}`manage-channels` - - -Following the release, Charmhub will display the charm's information at `charmhub.io/`. (The default information displayed is obtained from the most stable channel.) Your charm will also become available for download. - -> See more: [`juju` | Download a charm from Charmhub](https://juju.is/docs/juju/manage-charms-or-bundles#download-a-charmhub-charm) - - diff --git a/docs/howto/manage-revisions.rst b/docs/howto/manage-revisions.rst new file mode 100644 index 000000000..5f044dc35 --- /dev/null +++ b/docs/howto/manage-revisions.rst @@ -0,0 +1,79 @@ +.. _manage-charm-revisions: +How to manage charm revisions +============================= + +.. raw:: html + + + +Create a charm revision +----------------------- + +A charm revision is created implicitly every time you upload a charm to +Charmhub (unless you’re uploading the exact same file again). + + See more: :ref:`publish-a-charm` + +View the existing charm revisions +--------------------------------- + +To inspect the existing charm revisions, run ``charmcraft revisions`` +followed by the name of the charm. + + See more: :ref:`ref_commands_revisions` + +Promote a charm revision to a better risk level +----------------------------------------------- + +To promote a charm revision to a higher-ranking risk level, use the GitHub ``promote-charm`` action. + + See more: `GitHub \| + canonical/charming-actions/promote-charm `__ + +.. dropdown:: Example outcome + + For example, in the (partial) output of juju info mongodb below, + revision 100 has been promoted from ``3.6/edge`` through ``3.6/beta`` + and ``3.6/candidate`` all the way to ``3.6/stable``. (The up arrow next + to ``3.6/beta`` indicates that that channel has been closed and, if you + try ``juju deploy --channel 3.6/beta``, what you’ll get is the next + higher-ranking risk level of the same track, that is, + ``3.6/candidate``.) + + .. code:: text + + channels: | + 5/stable: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 + 5/candidate: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 + 5/beta: ↑ + 5/edge: 118 2023-05-03 (118) 13MB amd64 ubuntu@22.04 + 3.6/stable: 100 2023-04-28 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + 3.6/candidate: 100 2023-04-13 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + 3.6/beta: ↑ + 3.6/edge: 100 2023-02-03 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + + + +.. _release-a-revision-into-a-channel: +Release a charm revision into a channel +--------------------------------------- + +To release a specific charm revision to a channel, run ``charmcraft release`` followed by the name of the charm and flags +specifying the revision and its target channel. E.g., + +.. code:: text + + $ charmcraft release my-awesome-charm --revision=1 --channel=beta + Revision 1 of charm 'my-awesome-charm' released to beta + +.. + + See more: :ref:`ref_commands_release` + +This opens the channel you’re releasing to. + + See more: :ref:`manage-channels` + +Following the release, Charmhub will display the charm’s information at ``charmhub.io/``. (The default information displayed is obtained from the most stable channel.) Your charm will also become available for download. + + See more: `Juju \| Manage charms `_ diff --git a/docs/howto/manage-the-charmcraft-cli.md b/docs/howto/manage-the-charmcraft-cli.md deleted file mode 100644 index b4037f811..000000000 --- a/docs/howto/manage-the-charmcraft-cli.md +++ /dev/null @@ -1,119 +0,0 @@ -(manage-the-charmcraft-cli)= -# How to manage the `charmcraft` CLI - -> See first: {ref}`charmcraft-cli` - -## Install the `charmcraft` CLI - - -### On Linux - -The recommended way to install Charmcraft on Linux is from the `stable` channel via snap: - - sudo snap install charmcraft --classic - -There are multiple channels other than `stable`. See the full list with `snap info charmcraft`. - -We recommend either `latest/stable` or `latest/candidate` for everyday charming. With the snap you will always be up to date as Charmhub services and APIs evolve. Charmcraft supports Kubernetes operator development. - -In Linux, Charmcraft defaults to LXD to build the charms in a container matching the target base(s) (Multipass can also be used). Charmcraft will offer to install LXD if required, but here are steps to set it up manually: - -```text -$ sudo snap install lxd -$ sudo adduser $USER lxd -$ newgrp lxd -$ lxd init --auto -``` - -You can also install Charmcraft in an isolated environment. - -> See more: {ref}`install-in-an-isolated-environment` - -### On macOS - -Charmcraft is [available on homebrew](https://formulae.brew.sh/formula/charmcraft). - -Installation should be straightforward if using homebrew (if not already setup, refer to [this instructions](https://brew.sh/)). - -```text -$ brew install charmcraft -==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/manifests/1.3.2 -######################################################################## 100.0% -==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d96bbcfb72bdc -==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d -######################################################################## 100.0% -==> Pouring charmcraft--1.3.2.mojave.bottle.tar.gz -🍺 /usr/local/Cellar/charmcraft/1.3.2: 2,205 files, 17.2MB -``` - -Charmhub commands work natively: - -```text -$ charmcraft whoami -name: John Doe -username: jdoe -id: xxxxxxxxxxxxxxxxxxxxxxxxx -``` - -In macOS, Charmcraft defaults to Multipass to build the charms in a container matching the target base(s). Running pack asks to setup Multipass if not already installed, and continues with the packing process: - -```text -$ charmcraft pack -Multipass is required, but not installed. Do you wish to install Multipass and configure it with the defaults? [y/N]: y -==> Downloading https://github.com/canonical/multipass/releases/download/v1.7.2/multipass-1.7.2+mac-Darwin.pkg -Already downloaded: /Users/jdoe/Library/Caches/Homebrew/downloads/4237fcef800faa84459a2911c3818dfa76f1532d693b151438f1c8266318715b--multipass-1.7.2+mac-Darwin.pkg -==> Installing Cask multipass -==> Running installer for multipass; your password may be necessary. -Package installers may write to any location; options such as `--appdir` are ignored. -installer: Package name is multipass -installer: Installing at base path / -installer: The install was successful. -🍺 multipass was successfully installed! -Packing charm 'test-charm_ubuntu-20.04-amd64.charm'... -Starting charmcraft-test-charm-12886917363-0-0-amd64 ... -``` - -You can also install Charmcraft in an isolated environment. - -> See more: {ref}`install-in-an-isolated-environment` - - -### On Windows - -There is no previously packaged way to install Charmcraft in Windows, please refer to how to install it in an [isolated environment](#heading--isolated). - - -(install-in-an-isolated-environment)= -### In an isolated environment on Linux, macOS, or Windows - -One way to install Charmcraft is via [Multipass](https://multipass.run/). This is a good way to install it on any platform, as it will give you an isolated development environment. - -First, [install Multipass](https://multipass.run/docs/how-to-install-multipass). - -Second, use Multipass to provision a virtual machine. The following command will launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name 'charm-dev': - -```text -$ multipass launch --cpus 4 --memory 8G --disk 20G --name charm-dev -``` - -Last, open a shell in your new Ubuntu virtual machine, and install Charmcraft there: - -```text -$ multipass shell charm-dev -... -ubuntu@charm-dev:~$ sudo snap install charmcraft --classic -charmcraft 2.2.0 from Canonical✓ installed -``` - -That's it. You can now start typing in Charmcraft commands. - - -## Check the installed version of the `charmcraft` CLI - -To check the installed version, run: - -```text -charmcraft version -``` - -> See more: {ref}`ref_commands_version` diff --git a/docs/howto/manage-the-charmcraft-cli.rst b/docs/howto/manage-the-charmcraft-cli.rst new file mode 100644 index 000000000..d4a96cefd --- /dev/null +++ b/docs/howto/manage-the-charmcraft-cli.rst @@ -0,0 +1,138 @@ +.. _manage-the-charmcraft-cli: +How to manage the ``charmcraft`` CLI +==================================== + + See first: :ref:`charmcraft-cli` + +Install the ``charmcraft`` CLI +------------------------------ + +On Linux +~~~~~~~~ + +The recommended way to install Charmcraft on Linux is from the +``stable`` channel via snap: + +.. code:: text + + sudo snap install charmcraft --classic + +There are multiple channels other than ``stable``. See the full list +with ``snap info charmcraft``. + +We recommend either ``latest/stable`` or ``latest/candidate`` for +everyday charming. With the snap you will always be up to date as +Charmhub services and APIs evolve. Charmcraft supports Kubernetes +operator development. + +In Linux, Charmcraft defaults to LXD to build the charms in a container +matching the target base(s) (Multipass can also be used). Charmcraft +will offer to install LXD if required, but here are steps to set it up +manually: + +.. code:: text + + $ sudo snap install lxd + $ sudo adduser $USER lxd + $ newgrp lxd + $ lxd init --auto + +You can also install Charmcraft in an isolated environment. + + See more: :ref:`install-in-an-isolated-environment` + +On macOS +~~~~~~~~ + +Charmcraft is `available on homebrew `_. + +Installation should be straightforward if using homebrew (if not already set up, refer to `these instructions `_). + +.. code:: text + + $ brew install charmcraft + ==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/manifests/1.3.2 + ######################################################################## 100.0% + ==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d96bbcfb72bdc + ==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d + ######################################################################## 100.0% + ==> Pouring charmcraft--1.3.2.mojave.bottle.tar.gz + 🍺 /usr/local/Cellar/charmcraft/1.3.2: 2,205 files, 17.2MB + +Charmhub commands work natively: + +.. code:: text + + $ charmcraft whoami + name: John Doe + username: jdoe + id: xxxxxxxxxxxxxxxxxxxxxxxxx + +In macOS, Charmcraft defaults to Multipass to build the charms in a container matching the target base(s). Running pack asks to setup Multipass if not already installed, and continues with the packing process: + +.. code:: text + + $ charmcraft pack + Multipass is required, but not installed. Do you wish to install Multipass and configure it with the defaults? [y/N]: y + ==> Downloading https://github.com/canonical/multipass/releases/download/v1.7.2/multipass-1.7.2+mac-Darwin.pkg + Already downloaded: /Users/jdoe/Library/Caches/Homebrew/downloads/4237fcef800faa84459a2911c3818dfa76f1532d693b151438f1c8266318715b--multipass-1.7.2+mac-Darwin.pkg + ==> Installing Cask multipass + ==> Running installer for multipass; your password may be necessary. + Package installers may write to any location; options such as `--appdir` are ignored. + installer: Package name is multipass + installer: Installing at base path / + installer: The install was successful. + 🍺 multipass was successfully installed! + Packing charm 'test-charm_ubuntu-20.04-amd64.charm'... + Starting charmcraft-test-charm-12886917363-0-0-amd64 ... + +You can also install Charmcraft in an isolated environment. + + See more: :ref:`install-in-an-isolated-environment` + +On Windows +~~~~~~~~~~ + +There is no previously packaged way to install Charmcraft in Windows, +please refer to how to install it in an `isolated +environment <#heading--isolated>`_. + +.. _install-in-an-isolated-environment: +In an isolated environment on Linux, macOS, or Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Another way to install Charmcraft is via `Multipass `_. This is a good way to install it +on any platform, as it will give you an isolated development environment. + +First, `install Multipass `_. + +Second, use Multipass to provision a virtual machine. The following command will launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name ‘charm-dev’: + +.. code:: text + + $ multipass launch --cpus 4 --memory 8G --disk 20G --name charm-dev + +Last, open a shell in your new Ubuntu virtual machine, and install +Charmcraft there: + +.. code:: text + + $ multipass shell charm-dev + ... + ubuntu@charm-dev:~$ sudo snap install charmcraft --classic + charmcraft 2.2.0 from Canonical✓ installed + +That’s it. You can now start typing in Charmcraft commands. + +Check the installed version of the ``charmcraft`` CLI +----------------------------------------------------- + +To check the installed version, run: + +.. code:: text + + charmcraft version + +.. + + See more: :ref:`ref_commands_version` diff --git a/docs/howto/manage-the-current-charmhub-user.md b/docs/howto/manage-the-current-charmhub-user.md deleted file mode 100644 index bb3f9c597..000000000 --- a/docs/howto/manage-the-current-charmhub-user.md +++ /dev/null @@ -1,99 +0,0 @@ -(manage-the-current-charmhub-user)= -# How to manage the current Charmhub user -> See first: [`juju` | Charmhub](), [Charmhub](https://charmhub.io/) - -## Log in to Charmhub - -### Local environments - -To log in to Charmhub, run `charmcraft login`: - -``` -$ charmcraft login -Opening an authorization web page in your browser. -If it does not open, please open this URL: -... -``` - -> See more: {ref}`ref_commands_login` - - -### Remote environments - -> Introduced in Charmcraft 1.3 - -When working locally with Charmcraft, the developer will use `charmcraft login` to get authentication tokens from Charmhub, which would be stored in the local keyring and used on all operations that need them. - -This is fine for local environments, but for remote ones (e.g. a CI/CD system) on one hand it's desirable to not login using the standard method (a browser opening an authentication web page to insert user, password, and *2FA*), and on the other hand the authentication tokens should be limited in different ways. - -The alternative login method is implemented through the `CHARMCRAFT_AUTH` environment variable, which needs to be set to useful credentials (which are obtained using the `--export` option, see below). - -If that variable is set Charmcraft will use it for all operations that needs authentication against Charmhub. Note that in this case the `login` and `logout` commands can not be used. - -To obtain credentials to be used in `CHARMCRAFT_AUTH`, the `login` command has the `--export` option, which accepts a file path. If specified, it will override the regular behaviour of storing the credentials in the user's keyring, and those will be exported to the given file path. The content of this file is what should be used verbatim in the environment variable. - -As mentioned at the beginning, it's also a good idea to use restricted credentials in a remote system. For this situation, the Charmcraft's `login` command provides different options to attenuate the obtained authentication tokens: - -- `--charm`: the charm name on which the permission will apply (can be specified multiple times) -- `--bundle`: the bundle name on which the permission will apply (can be specified multiple times) -- `--permission`: what action can be done on the specified package(s) (see below for a list; can be specified multiple times) -- `--channel`: the channel on which the package can be released (can be specified multiple times) -- `--ttl`: the time, in seconds, that the granted token will be useful (defaults to 30 days) - -All these indications are optional, and default to no restrictions applied on each category (except indicated the time-to-live, as indicated above). Note also that these restrictions can only be used if the credentials are exported to a file with the `--export` option. - -The available permissions are: -- `account-register-package`: register/request a new package name -- `account-view-packages`: list packages owned by the account or for which this account has collaborator rights -- `package-manage`: meta permission that includes all the `package-manage-*` ones -- `package-manage-acl`: add/invite/remove collaborators -- `package-manage-metadata`: edit metadata, add/remove media, etc. -- `package-manage-releases`: release revisions and close channels -- `package-manage-revisions`: upload new blobs, check for upload status -- `package-view`: meta permission that includes all the `package-view-*` ones -- `package-view-acl`: list the collaborators for a package, return privacy settings -- `package-view-metadata`: view the metadata for a package, including media -- `package-view-metrics`: view the metrics of a package -- `package-view-releases`: list the current releases (channel map) and the history of releases for a package -- `package-view-revisions`: list the existing revisions for a package, along with status information - -So, an example sequence for requesting/using credentials to set up a CI/CD system that will push and release a charm could be: - -- get the specific credentials in a file: - ```bash - $ charmcraft login --export=secrets.auth --charm=my-super-charm --permission=package-manage --channel=edge --ttl=2592000 - Login successful. Credentials exported to 'secrets.auth'. - ``` -- test that all is fine; for this get the content: - ```bash - $ CHARMCRAFT_AUTH=`cat test1` charmcraft whoami - name: J. Doe - username: jdoe-superdev - id: VTLZAToLcdaIPtisVBjfiQYCXbpKwbCc - charms: - - my-super-charm - permissions: - - package-manage - channels: - - edge - time to live (s): 2592000 - ``` -- to use this authorization token on a CI/CD system set the environment variable CHARMCRAFT_AUTH with the content of `secrets.auth` file, and use Charmcraft as normal: - - ```bash - export CHARMCRAFT_AUTH= - ... - charmcraft upload my-super-charm.charm --release edge - ``` - -## Check currently logged in user - -To check the currently logged in user, run `charmcraft whoami`. - -> See more: {ref}`ref_commands_whoami` - -## Log out of Charmhub - -To log out of Charmhub, run `charmcraft logout`. - -> See more: {ref}`ref_commands_logout` diff --git a/docs/howto/manage-the-current-charmhub-user.rst b/docs/howto/manage-the-current-charmhub-user.rst new file mode 100644 index 000000000..bcaaf3541 --- /dev/null +++ b/docs/howto/manage-the-current-charmhub-user.rst @@ -0,0 +1,128 @@ +.. _manage-the-current-charmhub-user: +How to manage the current Charmhub user +======================================= + + See first: `Charmhub `_ + +Log in to Charmhub +------------------ + +Local environments +~~~~~~~~~~~~~~~~~~ + +To log in to Charmhub, run ``charmcraft login``: + +.. code:: text + + $ charmcraft login + Opening an authorization web page in your browser. + If it does not open, please open this URL: + ... + +.. + + See more: :ref:`ref_commands_login` + +Remote environments +~~~~~~~~~~~~~~~~~~~ + + Introduced in Charmcraft 1.3 + +When working locally with Charmcraft, the developer will use +``charmcraft login`` to get authentication tokens from Charmhub, which +would be stored in the local keyring and used on all operations that +need them. + +This is fine for local environments, but for remote ones (e.g. a CI/CD +system) on one hand it’s desirable to not login using the standard +method (a browser opening an authentication web page to insert user, +password, and *2FA*), and on the other hand the authentication tokens +should be limited in different ways. + +The alternative login method is implemented through the +``CHARMCRAFT_AUTH`` environment variable, which needs to be set to +useful credentials (which are obtained using the ``--export`` option, +see below). + +If that variable is set Charmcraft will use it for all operations that +needs authentication against Charmhub. Note that in this case the +``login`` and ``logout`` commands can not be used. + +To obtain credentials to be used in ``CHARMCRAFT_AUTH``, the ``login`` +command has the ``--export`` option, which accepts a file path. If +specified, it will override the regular behaviour of storing the +credentials in the user’s keyring, and those will be exported to the +given file path. The content of this file is what should be used +verbatim in the environment variable. + +As mentioned at the beginning, it’s also a good idea to use restricted +credentials in a remote system. For this situation, the Charmcraft’s +``login`` command provides different options to attenuate the obtained +authentication tokens: + +- ``--charm``: the charm name on which the permission will apply (can + be specified multiple times) +- ``--bundle``: the bundle name on which the permission will apply (can + be specified multiple times) +- ``--permission``: what action can be done on the specified package(s) + (see below for a list; can be specified multiple times) +- ``--channel``: the channel on which the package can be released (can + be specified multiple times) +- ``--ttl``: the time, in seconds, that the granted token will be + useful (defaults to 30 days) + +All these indications are optional, and default to no restrictions +applied on each category (except indicated the time-to-live, as +indicated above). Note also that these restrictions can only be used if +the credentials are exported to a file with the ``--export`` option. + +The available permissions are: - ``account-register-package``: +register/request a new package name - ``account-view-packages``: list +packages owned by the account or for which this account has collaborator +rights - ``package-manage``: meta permission that includes all the +``package-manage-*`` ones - ``package-manage-acl``: add/invite/remove +collaborators - ``package-manage-metadata``: edit metadata, add/remove +media, etc. - ``package-manage-releases``: release revisions and close +channels - ``package-manage-revisions``: upload new blobs, check for +upload status - ``package-view``: meta permission that includes all the +``package-view-*`` ones - ``package-view-acl``: list the collaborators +for a package, return privacy settings - ``package-view-metadata``: view +the metadata for a package, including media - ``package-view-metrics``: +view the metrics of a package - ``package-view-releases``: list the +current releases (channel map) and the history of releases for a package +- ``package-view-revisions``: list the existing revisions for a package, +along with status information + +So, an example sequence for requesting/using credentials to set up a +CI/CD system that will push and release a charm could be: + +- get the specific credentials in a file: + ``bash $ charmcraft login --export=secrets.auth --charm=my-super-charm --permission=package-manage --channel=edge --ttl=2592000 Login successful. Credentials exported to 'secrets.auth'.`` + +- test that all is fine; for this get the content: + :literal:`bash $ CHARMCRAFT_AUTH=`cat test1\` charmcraft whoami name: J. Doe username: jdoe-superdev id: VTLZAToLcdaIPtisVBjfiQYCXbpKwbCc charms: - my-super-charm permissions: - package-manage channels: - edge time to live (s): 2592000` + +- to use this authorization token on a CI/CD system set the environment + variable CHARMCRAFT_AUTH with the content of ``secrets.auth`` file, + and use Charmcraft as normal: + + .. code:: text + + export CHARMCRAFT_AUTH= + ... + charmcraft upload my-super-charm.charm --release edge + + +Check the currently logged in user +---------------------------------- + +To check the currently logged in user, run ``charmcraft whoami``. + + See more: :ref:`ref_commands_whoami` + +Log out of Charmhub +------------------- + +To log out of Charmhub, run ``charmcraft logout``. + + See more: :ref:`ref_commands_logout` diff --git a/docs/howto/manage-tracks.md b/docs/howto/manage-tracks.md deleted file mode 100644 index d26b910a0..000000000 --- a/docs/howto/manage-tracks.md +++ /dev/null @@ -1,101 +0,0 @@ -(manage-tracks)= -# How to manage tracks - -> See also: {ref}`track` - -When you register a charm name on Charmhub, you automatically get 4 channels, all with track `latest`. However, as your charm evolves, you'll likely want to customise the shape of this track (e.g., to align with the workload) and then create new tracks in the new pattern. This document shows you how. - -(request-a-track-guardrail)= -## Request a track guardrail -> See also: {ref}`guardrail` - -To request a track guardrail, contact a Charmhub admin by creating a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . - - -(create-a-track)= -## Create a track - -Once you've requested a track guardrail, there are two ways to create a new track for your charm -- you can keep contacting a Charmhub admin every time or you can self-service. For most cases the latter option is likely to be more convenient and faster. - -### Ask a Charmhub admin - -To create a new track by contacting a Charmhub admin, create a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . The admin will create the new track that fits within the track guardrail you’ve set up for your charm. - -### Create it yourself - -To create a new track yourself, follow the steps below: - -```{important} - -As you might notice, this path is currently a little hacky. In the long-term it should become a lot smoother as there are plans to support it through the Charmcraft CLI. - -``` - -```{important} - -As you will see, this method currently relies on `charmcraft`+ `curl`. We recommend the Charmcraft bit because Charmcraft already understands the authentication mechanism used by Charmhub and can generate a suitable authentication token (macaroon) that will make it possible to then use `curl` directly to interact with the Charmhub API. This method also has the advantage that it can be adapted to use any HTTP client or library as long as it can pass custom headers. - -``` - - -**1. Enable `curl` access to the Charmhub API.** - -First, install `curl` and `jq`. - -```{important} - -You might already have both. - -``` - -Then, use Charmcraft to log in to Charmhub and export your Charmhub credentials / token (macaroon) to a file: - -```text -charmcraft login --export charmhub-creds.dat -``` - -Next, decode and extract the macaroon from the .dat file and place it in a header in an environment variable: - -```text -export CHARMHUB_MACAROON_HEADER="Authorization: Macaroon $(cat charmhub-creds.dat | base64 -d | jq -r .v)" -``` - -At this point you can use this variable in `curl` commands -- just make sure to specify the correct `Content-Type`. - -**2. Use `curl` to view the existing guardrails and tracks.** To view the guardrails and tracks associated with your charm, issue an HTTP `GET` request to `/v1//`. For example, for a charm named `hello-world-charm`: - -```text -curl https://api.charmhub.io/v1/charm/hello-world-charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -``` - -The guardrails and tracks of the package will be under the `track-guardrails` and `tracks` keys of `metadata`. Now you know what the new track may look like. - -> See more: [Charmhub API docs > `package_metadata`](https://api.charmhub.io/docs/default.html#package_metadata) - -````{important} - -**If you want to view the guardrails and tracks for *all* published charms:** Issue an HTTP `GET` request to `/v1/`, as below: - -```text -curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -``` - -> See more: [Charmhub API docs > `list_registered_names`](https://api.charmhub.io/docs/default.html#list_registered_names) - - -```` - - -**3. Use `curl` to create a new track.** Finally, to create a new track for your charm, issue an HTTP `POST` request to `/v1///tracks`, where `name` and `namespace` refer to the name and type of the package respectively. For example, given a charm named `hello-world-charm`, one can create two tracks `v.1` and `v.2` as follows: - -```text -curl https://api.charmhub.io/v1/charm/hello-world-charm/tracks -X POST -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -d '[{"name": "v.1"}, {"name": "v.2"}]' -``` - -Of course, the tracks must conform to the existing guardrail for the charm. - - -> See more: [Charmhub API docs > `create_tracks`](https://api.charmhub.io/docs/default.html#create_tracks) - - -That's it, you now have a new track for your charm! diff --git a/docs/howto/manage-tracks.rst b/docs/howto/manage-tracks.rst new file mode 100644 index 000000000..91fae6098 --- /dev/null +++ b/docs/howto/manage-tracks.rst @@ -0,0 +1,104 @@ +.. _manage-tracks: +How to manage tracks +==================== + +.. See also: :ref:`track` Add link to Juju docs > charm > channel > track + +When you register a charm name on Charmhub, you automatically get 4 +channels, all with track ``latest``. However, as your charm evolves, +you’ll likely want to customise the shape of this track (e.g., to align +with the workload) and then create new tracks in the new pattern. This +document shows you how. + +.. _request-a-track-guardrail: +Request a track guardrail +------------------------- + +.. See also: :ref:`guardrail` (link to new Juju docs > charm > channel > track > guardrail) + +To request a track guardrail, contact a Charmhub admin by creating a +post on Discourse under the **charmhub requests** category, that is, +here: :literalref:`https://discourse.charmhub.io/c/charmhub-requests`. + +.. _create-a-track: +Create a track +-------------- + +Once you’ve requested a track guardrail, there are two ways to create a +new track for your charm – you can keep contacting a Charmhub admin +every time or you can self-service. For most cases the latter option is +likely to be more convenient and faster. + +Ask a Charmhub admin +~~~~~~~~~~~~~~~~~~~~ + +To create a new track by contacting a Charmhub admin, create a post on +Discourse under the **charmhub requests** category, that is, here: +https://discourse.charmhub.io/c/charmhub-requests/46 . The admin will +create the new track that fits within the track guardrail you’ve set up +for your charm. + +Create it yourself +~~~~~~~~~~~~~~~~~~ + +To create a new track yourself, follow the steps below: + +.. important:: + As you might notice, this path is currently a little hacky. In the long-term it should become a lot smoother as there are plans to support it through the Charmcraft CLI. + +.. important:: + As you will see, this method currently relies on `charmcraft`+ `curl`. We recommend the Charmcraft bit because Charmcraft already understands the authentication mechanism used by Charmhub and can generate a suitable authentication token (macaroon) that will make it possible to then use `curl` directly to interact with the Charmhub API. This method also has the advantage that it can be adapted to use any HTTP client or library as long as it can pass custom headers. + +1. Enable ``curl`` access to the Charmhub API. + +First, install ``curl`` and ``jq``. + +.. note:: + You might already have both. + + +Then, use Charmcraft to log in to Charmhub and export your Charmhub credentials / token (macaroon) to a file: + +.. code:: text + + charmcraft login --export charmhub-creds.dat + +Next, decode and extract the macaroon from the .dat file and place it in a header in an environment variable: + +.. code:: text + + export CHARMHUB_MACAROON_HEADER="Authorization: Macaroon $(cat charmhub-creds.dat | base64 -d | jq -r .v)" + +At this point you can use this variable in ``curl`` commands – just make sure to specify the correct ``Content-Type``. + +**2. Use ``curl`` to view the existing guardrails and tracks.** To view the guardrails and tracks associated with your charm, issue an HTTP ``GET`` request to ``/v1//``. For example, for a charm named ``hello-world-charm``: + +.. code:: text + + curl https://api.charmhub.io/v1/charm/hello-world-charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" + +The guardrails and tracks of the package will be under the ``track-guardrails`` and ``tracks`` keys of ``metadata``. Now you know +what the new track may look like. + + See more: `Charmhub API docs > package\_metadata `_ + +.. important:: **If you want to view the guardrails and tracks for all published charms:** + Issue an HTTP ``GET`` request to ``/v1/``, as below. See more: `Charmhub API docs > list_registered_names `_. + + .. code:: text + + curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" + + +3. Use ``curl`` to create a new track. Finally, to create a new track for your charm, issue an HTTP ``POST`` request to ``/v1///tracks``, where ``name`` and ``namespace`` refer to the name and type of the package respectively. For example, given a charm named ``hello-world-charm``, one can create two tracks ``v.1`` and ``v.2`` as follows: + +.. code:: text + + curl https://api.charmhub.io/v1/charm/hello-world-charm/tracks -X POST -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -d '[{"name": "v.1"}, {"name": "v.2"}]' + +Of course, the tracks must conform to the existing guardrail for the +charm. + + See more: `Charmhub API docs > create_tracks `_ + +That’s it, you now have a new track for your charm! diff --git a/docs/howto/misc/index.md b/docs/howto/misc/index.md deleted file mode 100644 index 37fd765c0..000000000 --- a/docs/howto/misc/index.md +++ /dev/null @@ -1,16 +0,0 @@ -(howto-misc)= -# Miscellaneous - -```{toctree} -:maxdepth: 2 - -Migrate to `poetry` -Migrate to `python` -Cache intermediate build artefacts -Pack a hook-based charm with `charmcraft` -Pack a reactive-based charm with `charmcraft` -``` - - diff --git a/docs/howto/misc/index.rst b/docs/howto/misc/index.rst new file mode 100644 index 000000000..a9b12eb28 --- /dev/null +++ b/docs/howto/misc/index.rst @@ -0,0 +1,12 @@ +.. howto-misc: +Miscellaneous +============= + +.. toctree:: + :maxdepth: 2 + + Migrate to poetry + Migrate to python + Cache intermediate build artefacts + Pack a hook-based charm with Charmcraft + Pack a reactive-based charm with Charmcraft diff --git a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md deleted file mode 100644 index 82cd53096..000000000 --- a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md +++ /dev/null @@ -1,158 +0,0 @@ -(pack-a-hooks-based-charm-with-charmcraft.md)= -# How to pack a hooks-based charm with Charmcraft - -> See also: -> - {ref}`How to set up a charm project ` -> - {ref}`About charm types, by creation type ` -> - {ref}`How to turn a hooks-based charm into an ops charm ` - -You want a hooks-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. - -```{note} - - Introduced in Charmcraft 1.4 - -``` - - - - -Suppose you have a hooks-only charm, for example, [tiny-bash](https://github.com/erik78se/tiny-bash), which you can obtain as follows: - - -```text -$ git clone https://github.com/erik78se/tiny-bash -``` - - - -To make it packable by Charmcraft, all you need to do is navigate inside the charm directory and create a `charmcraft.yaml` file with the part definition for a hooks-based charm, as shown below: - - -```yaml -type: charm - -bases: - - build-on: - - name: "ubuntu" - channel: "20.04" - run-on: - - name: "ubuntu" - channel: "20.04" - -parts: - tiny-bash: - plugin: dump - source: . - prime: - - LICENSE - - README.md - - config.yaml - - copyright - - hooks - - icon.svg - - metadata.yaml -``` - - - -After this, you can pack your charm with `charmcraft pack`, as usual: - -```text -$ charmcraft pack -Charms packed: - tiny-bash_ubuntu-20.04-amd64.charm -``` - -If successful, the result should look as below, i.e., the charm file should contain all the files listed in the `prime` section of the `tiny-bash` part and the charm manifest. - -```shell -$ unzip -l tiny-bash_ubuntu-20.04-amd64.charm -Archive: tiny-bash_ubuntu-20.04-amd64.charm - Length Date Time Name ---------- ---------- ----- ---- - 423 2021-11-12 19:37 metadata.yaml - 431 2021-11-12 19:37 README.md - 12 2021-11-12 19:37 config.yaml - 3693 2021-11-12 19:37 icon.svg - 38 2021-11-12 19:37 copyright - 261 2021-11-12 20:08 manifest.yaml - 34523 2021-11-12 19:37 LICENSE - 381 2021-11-12 19:37 hooks/update-status - 346 2021-11-12 19:37 hooks/start - 1294 2021-11-12 19:37 hooks/shared-fs-relation-changed - 563 2021-11-12 19:37 hooks/stop - 497 2021-11-12 19:37 hooks/leader-elected - 447 2021-11-12 19:37 hooks/install - 417 2021-11-12 19:37 hooks/leader-settings-changed - 811 2021-11-12 19:37 hooks/upgrade-charm - 625 2021-11-12 19:37 hooks/config-changed ---------- ------- - 44762 16 files -``` - - - -And you can also deploy your application with `juju deploy`, as usual: - -```shell -$ juju deploy ./tiny-bash_ubuntu-20.04-amd64.charm -Located local charm "tiny-bash", revision 0 -Deploying "tiny-bash" from local charm "tiny-bash", revision 0 -``` -If successful, the result should look as below, i.e., with the application status active. - -```text -$ juju status -Model Controller Cloud/Region Version SLA Timestamp -default localhost-localhost localhost/localhost 2.9.12 unsupported 17:23:23-03:00 - -App Version Status Scale Charm Store Channel Rev OS Message -tiny-bash active 1 tiny-bash local 0 ubuntu update-status ran: 20:22 - -Unit Workload Agent Machine Public address Ports Message -tiny-bash/0* active idle 0 10.2.17.31 update-status ran: 20:22 - -Machine State DNS Inst id Series AZ Message -0 started 10.2.17.31 juju-55481c-0 focal Running -``` - - - - diff --git a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst new file mode 100644 index 000000000..2ad7e499f --- /dev/null +++ b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst @@ -0,0 +1,106 @@ +.. _pack-a-hooks-based-charm-with-charmcraft: +How to pack a hooks-based charm with Charmcraft +=============================================== + + Introduced in Charmcraft 1.4 + + See first: `Ops \| Turn a hooks-based charm into an Ops charm `_ + +Suppose you have a legacy hooks-only charm, for example, [tiny-bash](https://github.com/erik78se/tiny-bash), which you can obtain as follows: + +.. code:: text + + $ git clone https://github.com/erik78se/tiny-bash + + +To make it packable by Charmcraft, all you need to do is navigate inside the charm directory and create a ``charmcraft.yaml`` file with the part definition for a hooks-based charm, as shown below: + +.. code:: yaml + + type: charm + + bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" + + parts: + tiny-bash: + plugin: dump + source: . + prime: + - LICENSE + - README.md + - config.yaml + - copyright + - hooks + - icon.svg + - metadata.yaml + + +After this, you can pack your charm with ``charmcraft pack``, as usual: + +.. code:: text + + $ charmcraft pack + Charms packed: + tiny-bash_ubuntu-20.04-amd64.charm + +If successful, the result should look as below, i.e., the charm file +should contain all the files listed in the ``prime`` section of the +``tiny-bash`` part and the charm manifest. + +.. code:: text + + $ unzip -l tiny-bash_ubuntu-20.04-amd64.charm + Archive: tiny-bash_ubuntu-20.04-amd64.charm + Length Date Time Name + --------- ---------- ----- ---- + 423 2021-11-12 19:37 metadata.yaml + 431 2021-11-12 19:37 README.md + 12 2021-11-12 19:37 config.yaml + 3693 2021-11-12 19:37 icon.svg + 38 2021-11-12 19:37 copyright + 261 2021-11-12 20:08 manifest.yaml + 34523 2021-11-12 19:37 LICENSE + 381 2021-11-12 19:37 hooks/update-status + 346 2021-11-12 19:37 hooks/start + 1294 2021-11-12 19:37 hooks/shared-fs-relation-changed + 563 2021-11-12 19:37 hooks/stop + 497 2021-11-12 19:37 hooks/leader-elected + 447 2021-11-12 19:37 hooks/install + 417 2021-11-12 19:37 hooks/leader-settings-changed + 811 2021-11-12 19:37 hooks/upgrade-charm + 625 2021-11-12 19:37 hooks/config-changed + --------- ------- + 44762 16 files + + +And you can also deploy your application with ``juju deploy``, as usual: + +.. code:: text + + $ juju deploy ./tiny-bash_ubuntu-20.04-amd64.charm + Located local charm "tiny-bash", revision 0 + Deploying "tiny-bash" from local charm "tiny-bash", revision 0 + +If successful, the result should look as below, i.e., with the application status active. + +.. code:: text + + $ juju status + Model Controller Cloud/Region Version SLA Timestamp + default localhost-localhost localhost/localhost 2.9.12 unsupported 17:23:23-03:00 + + App Version Status Scale Charm Store Channel Rev OS Message + tiny-bash active 1 tiny-bash local 0 ubuntu update-status ran: 20:22 + + Unit Workload Agent Machine Public address Ports Message + tiny-bash/0* active idle 0 10.2.17.31 update-status ran: 20:22 + + Machine State DNS Inst id Series AZ Message + 0 started 10.2.17.31 juju-55481c-0 focal Running + diff --git a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md deleted file mode 100644 index 29b5a0071..000000000 --- a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md +++ /dev/null @@ -1,126 +0,0 @@ -(pack-a-reactive-based-charm-with-charmcraft)= -# How to pack a reactive-based charm with Charmcraft - -> See also: -> - {ref}`How to set up a charm project ` -> - {ref}`How to pack your charm using Charmcraft ` -> - {ref}`About charm types, by creation type ` - - -Suppose you want a reactive-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. - -```{note} - - Introduced in Charmcraft 1.4. - -``` -```{note} - -The reactive way to write a charm represents an old standard. The recommended way to create a charm now is using {ref}`Charmcraft ` and {ref}`Ops `. - -``` - -To pack a reactive-based charm with Charmcraft, in the charm directory create a `charmcraft.yaml` file with the part definition for a reactive-based charm: - -```yaml -type: "charm" -bases: - - build-on: - - name: "ubuntu" - channel: "20.04" - run-on: - - name: "ubuntu" - channel: "20.04" -parts: - charm: - source: . - plugin: reactive - build-snaps: [charm] -``` - -Done. Now you can go ahead and pack your reactive-based charm with Charmcraft in the usual way using `charmcraft pack`. - - diff --git a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst new file mode 100644 index 000000000..fc1f5112f --- /dev/null +++ b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst @@ -0,0 +1,33 @@ +.. _pack-a-reactive-based-charm-with-charmcraft: +How to pack a reactive-based charm with Charmcraft +================================================== + + Introduced in Charmcraft 1.4. + + +.. See also: - +.. {ref}\ ``How to set up a charm project `` +.. - +.. {ref}\ ``How to pack your charm using Charmcraft `` +.. - {ref}\ ``About charm types, by creation type `` + +To pack a legacy reactive-based charm with Charmcraft, in the charm directory create a ``charmcraft.yaml`` file with the part definition for a reactive-based charm: + +.. code:: yaml + + type: "charm" + bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" + parts: + charm: + source: . + plugin: reactive + build-snaps: [charm] + +Done. Now you can go ahead and pack your reactive-based charm with Charmcraft in the usual way using ``charmcraft pack``. + diff --git a/docs/reference/charmcraft-cli.md b/docs/reference/charmcraft-cli.md deleted file mode 100644 index a5f6a3605..000000000 --- a/docs/reference/charmcraft-cli.md +++ /dev/null @@ -1,7 +0,0 @@ -(charmcraft-cli)= -# `charmcraft` CLI - -```{toctree} -:maxdepth:2 -list-of-charmcraft-cli-commands -``` diff --git a/docs/reference/charmcraft-cli.rst b/docs/reference/charmcraft-cli.rst new file mode 100644 index 000000000..c13477afe --- /dev/null +++ b/docs/reference/charmcraft-cli.rst @@ -0,0 +1,10 @@ +.. _charmcraft-cli: +``charmcraft`` CLI +================== + +.. toctree:: + :maxdepth: 2 + + list-of-charmcraft-cli-commands + + diff --git a/docs/reference/files/file-charmcraft-yaml.md b/docs/reference/files/file-charmcraft-yaml.md index 22e760730..946307446 100644 --- a/docs/reference/files/file-charmcraft-yaml.md +++ b/docs/reference/files/file-charmcraft-yaml.md @@ -1019,6 +1019,7 @@ parts: ``` +(file-charmcraft-yaml-peers-provides-requires)= ## `peers`, `provides`, `requires` > See also: [Juju | Relation (integration)](https://juju.is/docs/juju/relation) diff --git a/docs/reference/list-of-charmcraft-cli-commands.rst b/docs/reference/list-of-charmcraft-cli-commands.rst index 79d85ef4c..199942f78 100644 --- a/docs/reference/list-of-charmcraft-cli-commands.rst +++ b/docs/reference/list-of-charmcraft-cli-commands.rst @@ -1,7 +1,7 @@ .. _commands: List of ``charmcraft`` CLI commands -********************************* +*********************************** .. Use a hidden table of contents to ensure that documentation is read. From 267afd8d677118b8cb310242729f957d3f91e0e4 Mon Sep 17 00:00:00 2001 From: Erin Conley Date: Wed, 11 Dec 2024 16:15:39 -0500 Subject: [PATCH 03/19] docs: port Flask tutorial from Juju.is (#1976) The current Flask tutorial: https://juju.is/docs/sdk/write-your-first-kubernetes-charm-for-a-flask-app This PR will move the tutorial off of Discourse and Charm SDK and onto the Charmcraft RTD. --------- Co-authored-by: Michael DuBelko Co-authored-by: Alex Lowe --- docs/common/craft-parts | 1 - 1 file changed, 1 deletion(-) delete mode 120000 docs/common/craft-parts diff --git a/docs/common/craft-parts b/docs/common/craft-parts deleted file mode 120000 index 4aa111d9f..000000000 --- a/docs/common/craft-parts +++ /dev/null @@ -1 +0,0 @@ -/home/dora/git/charmcraft/.tox/docs/lib/python3.12/site-packages/craft_parts_docs/craft-parts \ No newline at end of file From 08ba2b8a6da47648cbabb4222ea705660c618765 Mon Sep 17 00:00:00 2001 From: Teodora Mihoc Date: Tue, 26 Nov 2024 17:32:40 +0100 Subject: [PATCH 04/19] docs: add, refactor, and reformat discourse docs feat. charmcraft --- docs/howto/index.md | 23 + docs/howto/manage-bundles.md | 40 ++ docs/howto/manage-channels.md | 75 +++ docs/howto/manage-charms.md | 507 ++++++++++++++++++ docs/howto/manage-extensions.md | 209 ++++++++ docs/howto/manage-icons.md | 70 +++ docs/howto/manage-libraries.md | 111 ++++ docs/howto/manage-names.md | 36 ++ docs/howto/manage-parts.md | 48 ++ docs/howto/manage-resources.md | 97 ++++ docs/howto/manage-revisions.md | 81 +++ docs/howto/manage-the-charmcraft-cli.md | 119 ++++ .../howto/manage-the-current-charmhub-user.md | 99 ++++ docs/howto/manage-tracks.md | 101 ++++ docs/howto/misc/index.md | 16 + ...ack-a-hooks-based-charm-with-charmcraft.md | 158 ++++++ ...-a-reactive-based-charm-with-charmcraft.md | 126 +++++ docs/reference/charmcraft-cli.md | 7 + 18 files changed, 1923 insertions(+) create mode 100644 docs/howto/index.md create mode 100644 docs/howto/manage-bundles.md create mode 100644 docs/howto/manage-channels.md create mode 100644 docs/howto/manage-charms.md create mode 100644 docs/howto/manage-extensions.md create mode 100644 docs/howto/manage-icons.md create mode 100644 docs/howto/manage-libraries.md create mode 100644 docs/howto/manage-names.md create mode 100644 docs/howto/manage-parts.md create mode 100644 docs/howto/manage-resources.md create mode 100644 docs/howto/manage-revisions.md create mode 100644 docs/howto/manage-the-charmcraft-cli.md create mode 100644 docs/howto/manage-the-current-charmhub-user.md create mode 100644 docs/howto/manage-tracks.md create mode 100644 docs/howto/misc/index.md create mode 100644 docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md create mode 100644 docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md create mode 100644 docs/reference/charmcraft-cli.md diff --git a/docs/howto/index.md b/docs/howto/index.md new file mode 100644 index 000000000..f4e977f3c --- /dev/null +++ b/docs/howto/index.md @@ -0,0 +1,23 @@ +(how-to-guides)= +# How-to guides + + +```{toctree} +:maxdepth: 2 + +Manage the `charmcraft` CLI +Manage charms +Manage charms (12-factor apps) +Manage extensions +Manage resources +Manage libraries +Manage parts +Manage the current Charmhub user +Manage names +Manage revisions +Manage channels +Manage tracks +Manage icons +Misc +Manage bundles +``` diff --git a/docs/howto/manage-bundles.md b/docs/howto/manage-bundles.md new file mode 100644 index 000000000..cacceafcd --- /dev/null +++ b/docs/howto/manage-bundles.md @@ -0,0 +1,40 @@ +(how-to-manage-charm-bundles)= +# How to manage charm bundles + +> See first: [`juju` | Bundle](https://juju.is/docs/juju/bundle) + +```{important} +Starting with 1 Jan 2025, bundles are being phased out. + +``` + +## Create a bundle + +To create a bundle, create a `.yaml` file with your desired configuration. + +```{tip} + +If you don't want to start from scratch, export the contents of your model to a `.yaml` file via `juju export-bundle --filename .yaml` or download the `.yaml` of an existing bundle from Charmhub. + +> See more: [Juju | How to compare and export the contents of a model to a bundle](https://juju.is/docs/juju/manage-models#heading--compare-and-export-the-contents-of-a-model-to-a-bundle) + + +``` + +> See more: {ref}`file-bundle-yaml` + +## Pack a bundle + +To pack a bundle, in the directory where you have your `bundle.yaml` file (and possibly other files, e.g., a `README.md` file), create a `charmcraft.yaml` file suitable for a bundle (at the minimum: `type: bundle`), then run `charmcraft pack` to pack the bundle. The result is a `.zip` file. + +> See more: {ref}`ref_commands_pack` + + +## Publish a bundle on Charmhub + +The process is identical to that for a simple charm except that, at the step where you register the name, for bundles the command is `register-bundle`. + + +> See more: {ref}`publish-a-charm` + + diff --git a/docs/howto/manage-channels.md b/docs/howto/manage-channels.md new file mode 100644 index 000000000..10c132c27 --- /dev/null +++ b/docs/howto/manage-channels.md @@ -0,0 +1,75 @@ +(manage-channels)= +# How to manage channels + + +## Create a channel + +When you register a name on Charmhub, that automatically creates 4 channels, all with track `latest` but with different risk levels, namely, `edge`, `beta`, `candidate`, `stable`, respectively. + +> See more: {ref}`register-a-name` + + + +## View the available channels + +To view a charm's channels on Charmhub, run `charmcraft status` followed by the name of the charm. E.g., + +```bash +$ charmcraft status my-awesome-charm +Track Channel Version Revision +latest stable - - + candidate - - + beta 0.1 1 + edge ↑ ↑ +``` + +(The above output shows 4 channels, all of which have the same track, `latest`, but different risk levels, namely, `edge`, `beta`, `candidate`, and `stable`.) + +> See more: {ref}`command-charmcraft-status` + + +## Customise a channel's track + + +Request a track guardrail and create a track + +See {ref}`manage-tracks`. + + +## Open a channel + + +A channel is opened implicitly when you release a revision to it. + + + +## Close a channel + +A channel is opened when you release a revision to that channel. Before that, the channel is created but not opened. When you're closing a channel, e.g., latest/candidate, that means that any deployment requests that go there will be forwarded to the next most stable risk, e.g., for beta, latest/stable. If you close stable, you can no longer deploy or update from that, unless you release again to that channel (because releasing opens the channel). + +If you add a branch, closing that branch will forward people to the same track and risk, without a branch. diff --git a/docs/howto/manage-charms.md b/docs/howto/manage-charms.md new file mode 100644 index 000000000..09a7f8ccb --- /dev/null +++ b/docs/howto/manage-charms.md @@ -0,0 +1,507 @@ +(manage-charms)= +# How to manage charms + +> See first: [`juju` | Charm](https://juju.is/docs/juju/charmed-operator), [`juju` | Manage charms](https://juju.is/docs/juju/manage-charms-or-bundles) + + +## Initialise a charm + +To initialise a charm project, create a directory for your charm, enter it, then run `charmcraft init` with the `--profile` flag followed by a suitable profile name (for machine charms: `machine`; for Kubernetes charms: `kubernetes`, `simple`, or `flask-framework`); that will create all the necessary files and even prepopulate them with useful content. + +```text +charmcraft init --profile +``` + + +````{dropdown} See sample session + + +```bash +$ mkdir my-flask-app-k8s +$ cd my-flask-app-k8s/ +$ charmcraft init --profile flask-framework +Charmed operator package file and directory tree initialised. + +Now edit the following package files to provide fundamental charm metadata +and other information: + +charmcraft.yaml +src/charm.py +README.md + +$ ls -R +.: +charmcraft.yaml requirements.txt src + +./src: +charm.py + + +``` + +```` + + + +The command also allows you to not specify any profile (in that case you get the `simple` profile -- a Kubernetes profile with lots of scaffolding, suitable for beginners) and has flags that you can use to specify a different directory to operate in, a charm name different from the name of the root directory, etc. + +> See more: {ref}`ref_commands_revisions`, {ref}`profile`, {ref}`list-of-files-in-a-charm-project` +> +> See more: {ref}`manage-extensions` + +## Add charm project metadata, an icon, docs + + +### Specify that the project is a charm + +To specify that the project is a charm (as supposed to a bundle), in your `charmcraft.yaml` file set the `type` key to `charm`: + +```text +type: charm +``` + +### Specify a name + +To specify a pack-and-deploy name for your charm, in your charm's `charmcraft.yaml` file specify the `name` key. E.g., + +```yaml +name: traefik-k8s +``` + +> See more: {ref}`file-charmcraft-yaml-name` + +### Specify a title + +To specify a title for your charm's page on Charmhub, in your charm's `charmcraft.yaml` file specify a value for the `title` key. E.g., + +```yaml +title: | + Traefik Ingress Operator for Kubernetes +``` + +> See more: {ref}`file-charmcraft-yaml-title` + +### Add a summary + +To add a summary line for your charm, in your charm's `charmcraft.yaml` file specify a value for the `summary` key. E.g., + +```yaml +summary: | + A Juju charm to run a Traefik-powered ingress controller on Kubernetes. +``` + +> See more: {ref}`file-charmcraft-yaml-summary` + +### Add a description + +To add a longer description for your charm, in your charm's `charmcraft.yaml` file specify a value for the `description` key. E.g., + +```yaml +description: | + A Juju-operated Traefik operator that routes requests from the outside of a + Kubernetes cluster to Juju units and applications. + +``` + +> See more: {ref}`file-charmcraft-yaml-description` + +### Add contact information + +To add maintainer contact information for a charm, in your charm's `charmcraft.yaml` file specify a value for the `links.contact` key. E.g., + +```yaml +links: + contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont +``` + +> See more: {ref}`file-charmcraft-yaml-contact` + +### Add a link to source code + +To add a link to the source code for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.source` key. E.g., + +```yaml +links: + source: + - https://github.com/canonical/traefik-k8s-operator +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add a link to the bug tracker + +To add a link to the bug tracker for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.issues` key. E.g., + +```yaml +links: + issues: + - https://github.com/canonical/traefik-k8s-operator/issues +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add a link to the website + +If your charm has a website outside of Charmhub, to add a link to this website, in your charm's `charmcraft.yaml` file specify an item under the `links.website` key. E.g., + +```yaml +links: + website: + - https://charmed-kubeflow.io/ +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add docs and a link to the docs + +If you publish your charm on Charmhub, reference documentation about the charm's resources, actions, configurations, relations, and libraries is extracted automatically. However, you should also aim to add further docs, e.g., a tutorial, how-to guides, etc. To provide a link to these docs, in your charm's `charmcraft.yaml` file specify a value for the `links.documentation` key. Note that at present this must be a Discourse page. E.g., + +```yaml +links: + documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 +``` + +> See more: {ref}`file-charmcraft-yaml-links` + +### Add terms of use + +To add terms of use for your charm, in your charm's `charmcraft.yaml` file specify a value for the `terms` key. E.g., + +```yaml +terms: +- Butterscotch is regal +- Cara is adorable +``` + +> See more: {ref}`file-charmcraft-yaml-terms` + + +### Add an icon + +See {ref}`manage-icons`. + + +## Add runtime details to a charm + +### Require a specific Juju version + +To require a specific Juju version for your charm, in your charm's `charmcraft.yaml` specify the `assumes` key. E.g., + +```yaml +assumes: + - juju >= 3.5 +``` + +> See more: {ref}`file-charmcraft-yaml-assumes` + +### Require a Kubernetes cloud + +To require a Kubernetes cloud for your charm, in your charm's `charmcraft.yaml` file specify the `assumes` key. E.g., + +```yaml +assumes: + - k8s-api +``` + +> See more: {ref}`file-charmcraft-yaml-assumes` + +### Require a specific base and platforms + +To require a specific base and platforms for your charm, in your charm's `charmcraft.yaml` file specify the `base`(,`build-base`,) and the `platforms keys. E.g., + +```{note} +In Charmcraft < 3.0 this was done via a single key: `bases`. + +``` + +```yaml +# The run time base, the base format is @, +# accepted bases are: +# - ubuntu@24.04 +base: +# The build time base, if not defined the base is also the build time +# base, in addition to valid bases, the build-base can be "devel" +# which would use the latest in development Ubuntu Series. +build-base: + +platforms: + # The supported platforms, may omit build-for if platform-name + # is a valid arch, valid architectures follow the Debian architecture names, + # accepted architectures are: + # - amd64 + # - arm64 + # - armhf + # - ppc64el + # - riscv64 + # - s390x + : + # The build time architecture + build-on: | + # The run time architecture + build-for: | +``` + +> See more: {ref}`file-charmcraft-yaml-base`, {ref}`build-base`, {ref}`file-charmcraft-yaml-platforms` + +### Specify container requirements + +To specify container requirements, in your charm's `charmcraft.yaml` file specify the `containers` key. + + +> See more: {ref}`file-charmcraft-yaml-containers` + + +### Specify associated resources + +See {ref}`manage-resources`. + +### Specify device requirements + +> See more: {ref}`file-charmcraft-yaml-devices` + +To specify container requirements, in your charm's `charmcraft.yaml` file specify the `devices` key. + +### Specify storage requirements + +To specify storage requirements, in your charm's `charmcraft.yaml` file specify the `storage` key. + +> See more: {ref}`file-charmcraft-yaml-storage` + +### Specify extra binding requirements + +To specify extra binding requirements, in your charm's `charmcraft.yaml` file specify the `extra-bindings` key. + +> See more: {ref}`file-charmcraft-yaml-extra-bindings` + +### Require subordinate deployment + +To require subordinate deployment for your charm (i.e., for it to be deployed to the same machine as another charm, called its 'principal'), in your charm's `charmcraft.yaml` file specify the `subordinate` key. + +> See more: {ref}`file-charmcraft-yaml-subordinate` + + +### Manage actions + +> See first: [`juju` | Action](https://juju.is/docs/juju/action), [`juju` | Manage actions](https://juju.is/docs/juju/manage-actions) + + +To declare an action in your charm, in your charm's `charmcraft.yaml` file specify the `actions` key. + +> See more: {ref}`file-charmcraft-yaml-actions` +> +> See next: [`ops` | Manage actions]() + + +### Manage configurations + +> See first: [`juju` | Application configuration](https://juju.is/docs/juju/configuration#heading--application-configuration), [`juju` | Manage applications > Configure](https://juju.is/docs/juju/manage-applications#configure-an-application) + +To declare a configuration option for your charm, in your charm's `charmcraft.yaml` specify the `config` key. + + +> See more: {ref}`file-charmcraft-yaml-config` +> +> See next: [`ops` | Manage configurations]() + + + +### Manage relations (integrations) + +> See first: [`juju` | Relation](https://juju.is/docs/juju/relation), [`juju` | Manage relations](https://juju.is/docs/juju/manage-relations) + +To declare a relation endpoint in your charm, in your charm's `charmcraft.yaml` specify the `peers`, `provides`, or `requires` key. + +> See more: {ref}`file-charmcraft-yaml-peers-provides-requires` +> +> See more: [`ops` | Manage relations (integrations)]() + + +### Specify necessary libs + +> See first: [`juju` | Library]() + + +See {ref}`manage-libraries`. + +### Manage secrets +> See first: [`juju` | User secret](https://juju.is/docs/juju/secret#heading--user) + +To make your charm capable of accepting a user secret, in your charm's `charmcraft.yaml` specify the `config` key with the `type` subkey set to `secret`. + +> See more: {ref}`file-charmcraft-yaml-config` +> +> See next: [`ops` | Manage secrets]() + +### Specify necessary parts + +See {ref}`manage-parts`. + +## Pack a charm + +To pack a charm directory, in the charm's root directory, run the command below: + +```text +charmcraft pack +``` + +This will fetch any dependencies (from PyPI, based on `requirements.txt`), compile any modules, check that all the key files are in place, and produce a compressed archive with the extension `.charm`. As you can verify, this archive is just a zip file with metadata and the operator code itself. + +````{dropdown} Expand to view a sample session for a charm called microsample-vm + + +```text +# Pack the charm: +~/microsample-vm$ charmcraft pack +Created 'microsample-vm_ubuntu-22.04-amd64.charm'. +Charms packed: + microsample-vm_ubuntu-22.04-amd64.charm + +# (Optional) Verify that this has created a .charm file in your charm's root directory: +~/microsample-vm$ ls +CONTRIBUTING.md charmcraft.yaml requirements.txt tox.ini +LICENSE microsample-vm_ubuntu-22.04-amd64.charm src +README.md pyproject.toml tests + +# (Optional) Verify that the .charm file is simply a zip file that contains +# everything you've packed plus any dependencies: +/microsample-vm$ unzip -l microsample-vm_ubuntu-22.04-amd64.charm | { head; tail;} +Archive: microsample-vm_ubuntu-22.04-amd64.charm + Length Date Time Name +--------- ---------- ----- ---- + 815 2023-12-05 12:12 README.md + 11337 2023-12-05 12:12 LICENSE + 250 2023-12-05 12:31 manifest.yaml + 102 2023-12-05 12:31 dispatch + 106 2023-12-01 14:59 config.yaml + 717 2023-12-05 12:31 metadata.yaml + 921 2023-12-05 12:26 src/charm.py + 817 2023-12-01 14:44 venv/setuptools/command/__pycache__/upload.cpython-310.pyc + 65175 2023-12-01 14:44 venv/setuptools/command/__pycache__/easy_install.cpython-310.pyc + 4540 2023-12-01 14:44 venv/setuptools/command/__pycache__/py36compat.cpython-310.pyc + 1593 2023-12-01 14:44 venv/setuptools/command/__pycache__/bdist_rpm.cpython-310.pyc + 6959 2023-12-01 14:44 venv/setuptools/command/__pycache__/sdist.cpython-310.pyc + 2511 2023-12-01 14:44 venv/setuptools/command/__pycache__/rotate.cpython-310.pyc + 2407 2023-12-01 14:44 venv/setuptools/extern/__init__.py + 2939 2023-12-01 14:44 venv/setuptools/extern/__pycache__/__init__.cpython-310.pyc +--------- ------- + 20274163 1538 files + + +``` + +```` + +The command has a number of flags that allow you to specify a different charm directory to pack, whether to force pack if there are linting errors, etc. + +> See more: {ref}`ref_commands_pack` + +```{caution} + +**If you've declared any resources :** This will *not* pack the resources. This means that, when you upload your charm to Charmhub (if you do), you will have to upload the resources separately. + +> See more: {ref}`manage-resources` + +``` + +```{important} + +When the charm is packed, a series of analyses and lintings will happen, you may receive warnings and even errors to help improve the quality of the charm. + +> See more: {ref}`Charmcraft analyzers and linters ` + +``` + +> See next: [`juju` | Deploy a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Debug a charm](), [`juju` | Update a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-local-charm) + +(publish-a-charm)= +## Publish a charm on Charmhub + +1. Log in to Charmhub: + +```text +charmcraft login +``` + +> See more: {ref}`manage-the-current-charmhub-user` + +2. Register your charm's name (the one you specified in `charmcraft.yaml` > `name`): + +```text +charmcraft register my-awesome-charm +``` + +> See more: {ref}`manage-names` + +```{note} +This automatically creates 4 channels, all with track latest but with different risk levels, namely, edge, beta, candidate, stable, respectively. See more: {ref}`manage-channels`. +``` + +3. Upload the charm to Charmhub: Use the `charmcraft upload` command followed by the your charm's filepath. E.g., if you are in the charm's root directory, + +```text +charmcraft upload my-awesome-charm.charm +Revision 1 of my-awesome-charm created +``` + +> See more: {ref}`ref_commands_upload` + +```{note} +Each time you upload a charm to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-charm-revisions`. +``` + + +4. If your charm has associated resources: These are not packed with the rest of the charm project, so you must upload them explicitly to Charmhub as well. For example: + +```text +$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin +Revision 1 created of resource 'someresource' for charm 'my-super-charm' +``` + +> See more: {ref}`manage-resources` + +```{note} +Each time you upload a resource to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-resource-revisions`. +``` + +5. Release the charm: To release a charm, release your revision of choice to the target release channel. E.g., + +```text +$ charmcraft release my-awesome-charm --revision=1 --channel=beta +Revision 1 of charm 'my-awesome-charm' released to beta +``` + +> See more: {ref}`manage-charm-revisions` + +```{note} + +This automatically opens the channel. See more: {ref}`manage-channels`. +``` + +> See next: [`juju` | Deploy a Charmub charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Update a Charmhub charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-charmhub-charm) + +```{tip} +To update the charm on Charmhub, repeat the upload and release steps. + +``` + + +```{important} + +Releasing a charm on Charmhub gives it a public URL. However, the charm will not appear in the Charmhub search results until it has passed formal review. To request formal review, reach out to the community to announce your charm and ask for a review by an experienced community member. + +> See more: [Discourse | review requests](https://discourse.charmhub.io/c/charmhub-requests/46) + + +Also, the point of publishing and having a charm publicly listed on Charmhub is so others can reuse it and potentially contribute to it as well. To publicize your charm: + +- [Write a Discourse post to announce your release.](https://discourse.charmhub.io/tags/c/announcements-and-community/33/none) + +- [Schedule a community workshop to demo your charm's capabilities.](https://discourse.charmhub.io/tag/community-workshop) + +- [Chat about it with your charmer friends.](https://matrix.to/#/#charmhub-charmdev:ubuntu.com) + + +``` + + + diff --git a/docs/howto/manage-extensions.md b/docs/howto/manage-extensions.md new file mode 100644 index 000000000..ae16f8fa9 --- /dev/null +++ b/docs/howto/manage-extensions.md @@ -0,0 +1,209 @@ +(manage-extensions)= +# How to manage extensions + +> See also: {ref}`extension` + +## View all the available extensions + +To view all the available Rockcraft / Charmcraft extensions, run the `rockcraft list-extensions` / `charmcraft list-extensions` command. Sample session: + +```bash +$ charmcraft list-extensions +Extension name Supported bases Experimental bases +---------------- ----------------- -------------------- +flask-framework ubuntu@22.04 +``` + +> See more: [Rockcraft | `rockcraft list-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/list-extensions/), {ref}`ref_commands_list-extensions` + +## View details about the extension in use + +Suppose you've initialised a rock / charm with a profile that comes with an extension (currently, `flask-framework`), and your `rockcraft.yaml` / `charmcraft.yaml > extensions` lists this extension. + + +````{dropdown} See example context + +```bash +$ mkdir my-flask-app-k8s +$ cd my-flask-app-k8s/ +$ charmcraft init --profile flask-framework +Charmed operator package file and directory tree initialised. + +Now edit the following package files to provide fundamental charm metadata +and other information: + +charmcraft.yaml +src/charm.py +README.md + +user@ubuntu:~/my-flask-app-k8s$ ls -R +.: +charmcraft.yaml requirements.txt src + +./src: +charm.py + +$ cat charmcraft.yaml +# This file configures Charmcraft. +# See https://juju.is/docs/sdk/charmcraft-config for guidance. + +name: my-flask-app-k8s + +type: charm + +bases: + - build-on: + - name: ubuntu + channel: "22.04" + run-on: + - name: ubuntu + channel: "22.04" + +# (Required) +summary: A very short one-line summary of the flask application. + +# (Required) +description: | + A comprehensive overview of your Flask application. + +extensions: + - flask-framework + +# Uncomment the integrations used by your application +# requires: +# mysql: +# interface: mysql_client +# limit: 1 +# postgresql: +# interface: postgresql_client +# limit: 1 + + +``` + +```` + +To view details about what that extension is adding to your charm, set the `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS` to `1`, then run `charmcraft expand-extensions`. For example: + + +```text +CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions +``` + + +````{dropdown} See effect given example context + +``` +$ CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions +*EXPERIMENTAL* extension 'flask-framework' enabled +name: my-flask-app-k8s +summary: A very short one-line summary of the flask application. +description: | + A comprehensive overview of your Flask application. +parts: + charm: + source: . + charm-entrypoint: src/charm.py + charm-binary-python-packages: [] + charm-python-packages: [] + charm-requirements: + - requirements.txt + charm-strict-dependencies: false + plugin: charm +type: charm +bases: +- build-on: + - name: ubuntu + channel: '22.04' + run-on: + - name: ubuntu + channel: '22.04' +actions: + rotate-secret-key: + description: Rotate the flask secret key. Users will be forced to log in again. + This might be useful if a security breach occurs. +assumes: +- k8s-api +containers: + flask-app: + resource: flask-app-image +peers: + secret-storage: + interface: secret-storage +provides: + metrics-endpoint: + interface: prometheus_scrape + grafana-dashboard: + interface: grafana_dashboard +requires: + logging: + interface: loki_push_api + ingress: + interface: ingress + limit: 1 +resources: + flask-app-image: + type: oci-image + description: flask application image. +config: + options: + webserver-keepalive: + type: int + description: Time in seconds for webserver to wait for requests on a Keep-Alive + connection. + webserver-threads: + type: int + description: Run each webserver worker with the specified number of threads. + webserver-timeout: + type: int + description: Time in seconds to kill and restart silent webserver workers. + webserver-workers: + type: int + description: The number of webserver worker processes for handling requests. + flask-application-root: + type: string + description: Path in which the application / web server is mounted. This configuration + will set the FLASK_APPLICATION_ROOT environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-debug: + type: boolean + description: Whether Flask debug mode is enabled. + flask-env: + type: string + description: What environment the Flask app is running in, by default it's 'production'. + flask-permanent-session-lifetime: + type: int + description: Time in seconds for the cookie to expire in the Flask application + permanent sessions. This configuration will set the FLASK_PERMANENT_SESSION_LIFETIME + environment variable. Run `app.config.from_prefixed_env()` in your Flask application + in order to receive this configuration. + flask-preferred-url-scheme: + type: string + default: HTTPS + description: Scheme for generating external URLs when not in a request context + in the Flask application. By default, it's "HTTPS". This configuration will + set the FLASK_PREFERRED_URL_SCHEME environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-secret-key: + type: string + description: The secret key used for securely signing the session cookie and + for any other security related needs by your Flask application. This configuration + will set the FLASK_SECRET_KEY environment variable. Run `app.config.from_prefixed_env()` + in your Flask application in order to receive this configuration. + flask-session-cookie-secure: + type: boolean + description: Set the secure attribute in the Flask application cookies. This + configuration will set the FLASK_SESSION_COOKIE_SECURE environment variable. + Run `app.config.from_prefixed_env()` in your Flask application in order to + receive this configuration. + +``` + +```` + + +> See more: [`rockcraft expand-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/expand-extensions/), {ref}`ref_commands_expand-extensions` + +
+ +> **Contributors:** @lengau, @tmihoc diff --git a/docs/howto/manage-icons.md b/docs/howto/manage-icons.md new file mode 100644 index 000000000..c03cfd0da --- /dev/null +++ b/docs/howto/manage-icons.md @@ -0,0 +1,70 @@ +(manage-icons)= +# How to manage icons + + +## Learn about icon requirements and best practices + +See {ref}`file-icon-svg`. + + +## Create an icon + +Before you start you will need: + +- A vector graphic editor. We strongly recommend the cross-platform and most excellent [Inkscape](http://www.inkscape.org) for all your vector graphic needs. +- [The template file.](https://assets.ubuntu.com/v1/fc0260eb-icon.svg) (right-click > Save link as...) +- An existing logo you can import, or the ability to draw one in Inkscape. + +Once you have those, fire up Inkscape and we can begin! + +### 1. Open the template + + +From Inkscape load the `icon.svg` file. Select the Layer called "Background Circle", either from the drop down at the bottom, or from the layer dialog. + +![Open the template](manage-icons-create-1.png) + +### 3. Add colour + +In the menu, select **Object** and then **Fill and Stroke** to adjust the colour. + +![Add color](manage-icons-create-2.png) + + +### 2. Draw something + + +Draw your shape within the circle. If you already have a vector logo, you can import it and scale it within the guides. Inkscape also has plenty of drawing tools for creating complex images. + +If you import a bitmap image to use, be sure to convert it into a vector file and delete the bitmap. + +![Draw something](manage-icons-create-3.png) + +*Cloud icon: "Cloud by unlimicon from the Noun Project" [CC BY]* + + + +## Validate an icon + +You can validate your icon at [charmhub.io/icon-validator](https://charmhub.io/icon-validator). The page checks the most basic issues that prevent icons working. + +![Validate](manage-icons-validate.png) + + +```{note} + + +``` + +## Add an icon to its charm's Charmhub page + +To add the icon to the charm's Charmhub page, save it as `icon.svg`, place it in the root directory of the charm, and then publish the charm to a channel of the form `/stable` (e.g., `latest/stable`). + +```{note} + +That is because Charmhub only updates the metadata for a charm on stable channel releases [(by design)](https://snapcraft.io/blog/better-snap-metadata-handling-coming-your-way-soon). +So either release the revision with the icon to a `stable` channel and then roll it back, or wait until your charm is ready for a "stable" `stable` release. + +``` + +> See more: {ref}`publish-a-charm` diff --git a/docs/howto/manage-libraries.md b/docs/howto/manage-libraries.md new file mode 100644 index 000000000..edfe1735c --- /dev/null +++ b/docs/howto/manage-libraries.md @@ -0,0 +1,111 @@ +(manage-libraries)= +# How to manage libraries +> See first: [`juju` | Library]() + + +## Initialise a library + +> See also: {ref}``charmcraft create-lib` ` + +In your charm's root directory, run `charmcraft create-lib`: + +```text +# Initialise a charm library named 'demo' +$ charmcraft create-lib demo +``` + +```{note} + +Before creating a library, you must first register ownership of your charm’s name. See more: {ref}`publish-a-charm`. + +``` + +This will create a template file at `$CHARMDIR/lib/charms/demo/v0/demo.py`. + + +> See more: {ref}`ref_commands_create-lib`, {ref}`file-libname-py` + +Edit this file to write your library. + +```{important} + +A library must comprise a single Python file. If you write a library that feels too "big" for a single file, it is likely that the library should be split up, or that you are actually writing a full-on charm. + +``` + +> See next: [`ops` | Manage libraries]() + + +(publish-a-library)= +## Publish a library on Charmhub + + +```{caution} + +On Charmhub, a library is always associated with the charm that it was first created for. When you publisht it to Charmhub, it's published to the page of that charm. To be able to publish it, you need to be logged in to Charmhub as a user who owns the charm (see more: {ref}`publish-a-charm`) or as a user who is registered as a contributor to the charm (a status that can be requested via [Discourse](https://discourse.charmhub.io/). + +``` + +To publish a library on Charmhub, in the root directory of the charm that holds the library, run `charmcraft publish-lib` followed by the full library path on the template `charms..v.`. For example: + +```text +$ charmcraft publish-lib charms.demo.v0.demo +Library charms.demo.v0.demo sent to the store with version 0.1 +``` + +> See more: {ref}`ref_commands_publish-lib` + +This will upload the library's content to Charmhub. + +To update the library on Charmhub, update the `LIBAPI`/`LIBPATCH` metadata fields inside the library file, then repeat the publish procedure. + +```{caution} **About the metadata fields:** + +Most times it is enough to just increment `LIBPATCH` but, if you're introducing breaking changes, you must work with the major API version. + +Additionally, be mindful of the fact that users of your library will update it automatically to the latest PATCH version with the same API version. To avoid breaking other people's library usage, make sure to increment the `LIBAPI` version but reset `LIBPATCH` to `0`. Also, before adding the breaking changes and updating these values, make sure to copy the library to the new path; this way you can maintain different major API versions independently, being able to update, for example, your v0 after publishing v1. See more: {ref}`file-libname-py`. +``` + +> See more: {ref}`ref_commands_publish-lib` + + +To share your library with other charm developers, navigate to the host charm's Charmhub page, go to Libraries tab, then copy and share the URL at the top of the page. + + +## View the libs published for a charm + +The easiest way to find an existing library for a given charm is via `charmcraft list-lib`, as shown below. This will query Charmhub and show which libraries are published for the specified charm, along with API/patch versions. + + jdoe@machine:/home/jane/autoblog$ charmcraft list-lib blogsystem + Library name API Patch + superlib 1 0 + +The listing will not show older API versions; this ensures that new users always start with the latest version. + +Another good way to search for libraries is to explore the charm collection on [Charmhub](https://charmhub.io/). + +> See more: {ref}``charmcraft list-lib` ` + + +## Use a library + +In your charm's `charmcraft.yaml`, specify the `charm-libs` key with the desired libraries. + +> See more: {ref}`file-charmcraft-yaml-charm-libs` + + +In your charm's root directory, run `charmcraft fetch-libs`. Charmcraft will download the libraries to your charm's directory. + +> See more: {ref}`ref_commands_fetch-libs` + + +To use a library in your `src/charm.py`, import it using its fully-qualified path minus the `lib` part: + +```python +import charms.demo.v0.demo +``` + +To update your lib with the latest published version, repeat the process. + + + diff --git a/docs/howto/manage-names.md b/docs/howto/manage-names.md new file mode 100644 index 000000000..916e17dc3 --- /dev/null +++ b/docs/howto/manage-names.md @@ -0,0 +1,36 @@ +(manage-names)= +# How to manage names + + +(register-a-name)= +## Register a name on Charmhub + + +To register a name for your charm on Charmhub, use the `chamrcraft register` command followed by your desired name. E.g., + +```bash +$ charmcraft register my-awesome-charm +Congrats! You are now the publisher of 'my-awesome-charm' +``` + +> See more: {ref}`ref_commands_register` + +This also automatically creates four channels, all with track `latest` but risk level `edge`, `beta`, `candidate`, and `stable`, respectively. + +> See more: {ref}`manage-channels` + +## View registered names + +To view the names you've registered on Charmhub, run `charmcraft names`. + +> See more: {ref}`ref_commands_names` + +## Unregister a name + +```{caution} +A name can be unregistered only if you haven't yet uploaded anything to it. +``` + +To unregister a name, run `charmcraft unregister` followed by the name. + +> See more: {ref}`ref_commands_unregister` diff --git a/docs/howto/manage-parts.md b/docs/howto/manage-parts.md new file mode 100644 index 000000000..f9da9c76b --- /dev/null +++ b/docs/howto/manage-parts.md @@ -0,0 +1,48 @@ +(manage-parts)= +# How to manage parts + +> See first: {ref}`part` + + +## Remove a part's assets + +TBA + +> See more: {ref}`ref_commands_clean` + + +## Download or retrieve artifacts defined for a part + +TBA + +> See more: {ref}`ref_commands_pull` + + +## Build artifacts defined for a part + +TBA + +> See more: {ref}`ref_commands_build` + + +## Stage built artifacts into a common staging area + +TBA + +> See more: {ref}`ref_commands_build` + + +## Prime artifacts defined for a part + +TBA + +> See more: {ref}`ref_commands_prime` + + +## Build the charm or bundle + + +TBA + +> See more: {ref}`ref_commands_pack` + diff --git a/docs/howto/manage-resources.md b/docs/howto/manage-resources.md new file mode 100644 index 000000000..b6edd18b8 --- /dev/null +++ b/docs/howto/manage-resources.md @@ -0,0 +1,97 @@ +manage-resources)= +# How to manage resources + +> See first: [`juju` | Charm resource](https://juju.is/docs/juju/charm-resource), [`juju` | Manage resources](https://juju.is/docs/juju/manage-charm-resources) + +## Declare a resource + +To declare a resource required by your charm, in your charm's `charmcraft.yaml file` specify the `resources` key. + +> See more: {ref}`file-charmcraft-yaml-resources` +> +> See next: [`ops` | Manage resources]() + + +````{tip} +During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. For example, assuming the resource is a `/tmp/somefile.txt` file, you could pack and the deploy with `juju deploy ... --resource`: + +```text +echo "TEST" > /tmp/somefile.txt +charmcraft pack +juju deploy ./my-charm.charm --resource my-resource=/tmp/somefile.txt +``` + +```` + +(publish-a-resource)= +## Publish a resource on Charmhub + +```{note} +You must have already published the charm. See more: {ref}`publish-a-charm`. + +``` + +To publish a resource on its charm's Charmhub page, run the `charmcraft upload-resource` command followed by the name of the charm, the name of the resource (cf. `charmcraft.yaml`), and `--filepath=` / `--image=`. For example: + +```{note} + +The option `--image` must indicate an OCI image's digest, being it in the short or long form (e.g.: `70aa8983ec5c` or `sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249`). When using the "short form" of the digest, the image needs to be present locally so its proper ID (the "long form") can be retrieved. + + +``` + + +```text +$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin +Revision 1 created of resource 'someresource' for charm 'my-super-charm' +``` + +```text +$ charmcraft upload-resource my-super-charm redis-image --image=sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249 +Revision 1 created of resource 'redis-image' for charm 'my-super-charm' +``` + +Charmcraft will first check if that specific image is available in Canonical's Registry, and just use it if that's the case. If not, it will try to get it from the developer's local OCI repository (needs `dockerd` to be installed and running), push it to the Canonical's Registry, and then use it. Either way, when the upload has completed, you end up with a resource revision. + +To update a pre-uploaded resource, run the `upload-resource` command again. The result will be a new revision. + +> See more: {ref}`ref_commands_upload-resource` + +## View all the resources published on Charmhub + +To view all the resources published on Charmhub for a charm, run `charmcraft resources` followed by the charm name: + +```{important} + +**If you're not logged in to Charmhub:** The command will open up a browser window and ask you to log in. + +``` + +```text +$ charmcraft resources mycharm +``` + +> See more: {ref}`ref_commands_resources` + +(manage-resource-revisions)= +## Manage resource revisions +### List all the available resource revisions + +To view all the revisions for a resource associated with a charm you've uploaded to Charmhub, run `charmcraft resource-revisions` followed by the charm name and the resource name. For example: + +```text +$ charmcraft resource-revisions mycharm myresource +``` + +> See more: {ref}`ref_commands_resource-revisions` + +### Set the architectures for a resource revision + +To set the architectures for a revision of a resource associated with a charm you've uploaded to Charmhub, run `charmcraft set-resource-architectures` followed by the name of the charm, the name of the resource, and the architecture(s), using the `--resources` flag to specify the target resource revision. For example: + +```text +$ charmcraft set-resource-architectures mycharm myresource --revision=1 arm64,armhf +``` + +> See more: {ref}`ref_commands_set-resource-architectures` + diff --git a/docs/howto/manage-revisions.md b/docs/howto/manage-revisions.md new file mode 100644 index 000000000..83bcecc9a --- /dev/null +++ b/docs/howto/manage-revisions.md @@ -0,0 +1,81 @@ +(manage-charm-revisions)= +# How to manage charm revisions + + + +## Create a charm revision + +A charm revision is created implicitly every time you upload a charm to Charmhub (unless you're uploading the exact same file again). + +> See more: {ref}`publish-a-charm` + +## View the existing charm revisions + + + To inspect the existing charm revisions, run `charmcraft revisions` followed by the name of the charm. + +> See more: {ref}`ref_commands_revisions` + + +## Promote a charm revision to a better risk level + +To promote a charm revision to a higher-ranking risk level, + + + +use the GitHub `promote-charm` action. + +> See more: [GitHub | canonical/charming-actions/promote-charm](https://github.com/canonical/charming-actions/tree/2.6.0/promote-charm) + +````{dropdown} Example outcome + +For example, in the (partial) output of juju info mongodb below, revision 100 has been promoted from `3.6/edge` through `3.6/beta` and `3.6/candidate` all the way to `3.6/stable`. (The up arrow next to `3.6/beta` indicates that that channel has been closed and, if you try `juju deploy --channel 3.6/beta`, what you’ll get is the next higher-ranking risk level of the same track, that is, `3.6/candidate`.) + +```text +channels: | + 5/stable: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 + 5/candidate: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 + 5/beta: ↑ + 5/edge: 118 2023-05-03 (118) 13MB amd64 ubuntu@22.04 + 3.6/stable: 100 2023-04-28 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + 3.6/candidate: 100 2023-04-13 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + 3.6/beta: ↑ + 3.6/edge: 100 2023-02-03 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + +``` +```` + + +(release-a-revision-into-a-channel)= +## Release a charm revision into a channel + +To release a specific charm revision to a channel, run `charmcraft release` followed by the name of the charm and flags specifying the revision and its target channel. E.g., + +```text +$ charmcraft release my-awesome-charm --revision=1 --channel=beta +Revision 1 of charm 'my-awesome-charm' released to beta +``` + +> See more: {ref}`ref_commands_release` + + +This opens the channel you're releasing to. + + +> See more: {ref}`manage-channels` + + +Following the release, Charmhub will display the charm's information at `charmhub.io/`. (The default information displayed is obtained from the most stable channel.) Your charm will also become available for download. + +> See more: [`juju` | Download a charm from Charmhub](https://juju.is/docs/juju/manage-charms-or-bundles#download-a-charmhub-charm) + + diff --git a/docs/howto/manage-the-charmcraft-cli.md b/docs/howto/manage-the-charmcraft-cli.md new file mode 100644 index 000000000..b4037f811 --- /dev/null +++ b/docs/howto/manage-the-charmcraft-cli.md @@ -0,0 +1,119 @@ +(manage-the-charmcraft-cli)= +# How to manage the `charmcraft` CLI + +> See first: {ref}`charmcraft-cli` + +## Install the `charmcraft` CLI + + +### On Linux + +The recommended way to install Charmcraft on Linux is from the `stable` channel via snap: + + sudo snap install charmcraft --classic + +There are multiple channels other than `stable`. See the full list with `snap info charmcraft`. + +We recommend either `latest/stable` or `latest/candidate` for everyday charming. With the snap you will always be up to date as Charmhub services and APIs evolve. Charmcraft supports Kubernetes operator development. + +In Linux, Charmcraft defaults to LXD to build the charms in a container matching the target base(s) (Multipass can also be used). Charmcraft will offer to install LXD if required, but here are steps to set it up manually: + +```text +$ sudo snap install lxd +$ sudo adduser $USER lxd +$ newgrp lxd +$ lxd init --auto +``` + +You can also install Charmcraft in an isolated environment. + +> See more: {ref}`install-in-an-isolated-environment` + +### On macOS + +Charmcraft is [available on homebrew](https://formulae.brew.sh/formula/charmcraft). + +Installation should be straightforward if using homebrew (if not already setup, refer to [this instructions](https://brew.sh/)). + +```text +$ brew install charmcraft +==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/manifests/1.3.2 +######################################################################## 100.0% +==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d96bbcfb72bdc +==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d +######################################################################## 100.0% +==> Pouring charmcraft--1.3.2.mojave.bottle.tar.gz +🍺 /usr/local/Cellar/charmcraft/1.3.2: 2,205 files, 17.2MB +``` + +Charmhub commands work natively: + +```text +$ charmcraft whoami +name: John Doe +username: jdoe +id: xxxxxxxxxxxxxxxxxxxxxxxxx +``` + +In macOS, Charmcraft defaults to Multipass to build the charms in a container matching the target base(s). Running pack asks to setup Multipass if not already installed, and continues with the packing process: + +```text +$ charmcraft pack +Multipass is required, but not installed. Do you wish to install Multipass and configure it with the defaults? [y/N]: y +==> Downloading https://github.com/canonical/multipass/releases/download/v1.7.2/multipass-1.7.2+mac-Darwin.pkg +Already downloaded: /Users/jdoe/Library/Caches/Homebrew/downloads/4237fcef800faa84459a2911c3818dfa76f1532d693b151438f1c8266318715b--multipass-1.7.2+mac-Darwin.pkg +==> Installing Cask multipass +==> Running installer for multipass; your password may be necessary. +Package installers may write to any location; options such as `--appdir` are ignored. +installer: Package name is multipass +installer: Installing at base path / +installer: The install was successful. +🍺 multipass was successfully installed! +Packing charm 'test-charm_ubuntu-20.04-amd64.charm'... +Starting charmcraft-test-charm-12886917363-0-0-amd64 ... +``` + +You can also install Charmcraft in an isolated environment. + +> See more: {ref}`install-in-an-isolated-environment` + + +### On Windows + +There is no previously packaged way to install Charmcraft in Windows, please refer to how to install it in an [isolated environment](#heading--isolated). + + +(install-in-an-isolated-environment)= +### In an isolated environment on Linux, macOS, or Windows + +One way to install Charmcraft is via [Multipass](https://multipass.run/). This is a good way to install it on any platform, as it will give you an isolated development environment. + +First, [install Multipass](https://multipass.run/docs/how-to-install-multipass). + +Second, use Multipass to provision a virtual machine. The following command will launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name 'charm-dev': + +```text +$ multipass launch --cpus 4 --memory 8G --disk 20G --name charm-dev +``` + +Last, open a shell in your new Ubuntu virtual machine, and install Charmcraft there: + +```text +$ multipass shell charm-dev +... +ubuntu@charm-dev:~$ sudo snap install charmcraft --classic +charmcraft 2.2.0 from Canonical✓ installed +``` + +That's it. You can now start typing in Charmcraft commands. + + +## Check the installed version of the `charmcraft` CLI + +To check the installed version, run: + +```text +charmcraft version +``` + +> See more: {ref}`ref_commands_version` diff --git a/docs/howto/manage-the-current-charmhub-user.md b/docs/howto/manage-the-current-charmhub-user.md new file mode 100644 index 000000000..bb3f9c597 --- /dev/null +++ b/docs/howto/manage-the-current-charmhub-user.md @@ -0,0 +1,99 @@ +(manage-the-current-charmhub-user)= +# How to manage the current Charmhub user +> See first: [`juju` | Charmhub](), [Charmhub](https://charmhub.io/) + +## Log in to Charmhub + +### Local environments + +To log in to Charmhub, run `charmcraft login`: + +``` +$ charmcraft login +Opening an authorization web page in your browser. +If it does not open, please open this URL: +... +``` + +> See more: {ref}`ref_commands_login` + + +### Remote environments + +> Introduced in Charmcraft 1.3 + +When working locally with Charmcraft, the developer will use `charmcraft login` to get authentication tokens from Charmhub, which would be stored in the local keyring and used on all operations that need them. + +This is fine for local environments, but for remote ones (e.g. a CI/CD system) on one hand it's desirable to not login using the standard method (a browser opening an authentication web page to insert user, password, and *2FA*), and on the other hand the authentication tokens should be limited in different ways. + +The alternative login method is implemented through the `CHARMCRAFT_AUTH` environment variable, which needs to be set to useful credentials (which are obtained using the `--export` option, see below). + +If that variable is set Charmcraft will use it for all operations that needs authentication against Charmhub. Note that in this case the `login` and `logout` commands can not be used. + +To obtain credentials to be used in `CHARMCRAFT_AUTH`, the `login` command has the `--export` option, which accepts a file path. If specified, it will override the regular behaviour of storing the credentials in the user's keyring, and those will be exported to the given file path. The content of this file is what should be used verbatim in the environment variable. + +As mentioned at the beginning, it's also a good idea to use restricted credentials in a remote system. For this situation, the Charmcraft's `login` command provides different options to attenuate the obtained authentication tokens: + +- `--charm`: the charm name on which the permission will apply (can be specified multiple times) +- `--bundle`: the bundle name on which the permission will apply (can be specified multiple times) +- `--permission`: what action can be done on the specified package(s) (see below for a list; can be specified multiple times) +- `--channel`: the channel on which the package can be released (can be specified multiple times) +- `--ttl`: the time, in seconds, that the granted token will be useful (defaults to 30 days) + +All these indications are optional, and default to no restrictions applied on each category (except indicated the time-to-live, as indicated above). Note also that these restrictions can only be used if the credentials are exported to a file with the `--export` option. + +The available permissions are: +- `account-register-package`: register/request a new package name +- `account-view-packages`: list packages owned by the account or for which this account has collaborator rights +- `package-manage`: meta permission that includes all the `package-manage-*` ones +- `package-manage-acl`: add/invite/remove collaborators +- `package-manage-metadata`: edit metadata, add/remove media, etc. +- `package-manage-releases`: release revisions and close channels +- `package-manage-revisions`: upload new blobs, check for upload status +- `package-view`: meta permission that includes all the `package-view-*` ones +- `package-view-acl`: list the collaborators for a package, return privacy settings +- `package-view-metadata`: view the metadata for a package, including media +- `package-view-metrics`: view the metrics of a package +- `package-view-releases`: list the current releases (channel map) and the history of releases for a package +- `package-view-revisions`: list the existing revisions for a package, along with status information + +So, an example sequence for requesting/using credentials to set up a CI/CD system that will push and release a charm could be: + +- get the specific credentials in a file: + ```bash + $ charmcraft login --export=secrets.auth --charm=my-super-charm --permission=package-manage --channel=edge --ttl=2592000 + Login successful. Credentials exported to 'secrets.auth'. + ``` +- test that all is fine; for this get the content: + ```bash + $ CHARMCRAFT_AUTH=`cat test1` charmcraft whoami + name: J. Doe + username: jdoe-superdev + id: VTLZAToLcdaIPtisVBjfiQYCXbpKwbCc + charms: + - my-super-charm + permissions: + - package-manage + channels: + - edge + time to live (s): 2592000 + ``` +- to use this authorization token on a CI/CD system set the environment variable CHARMCRAFT_AUTH with the content of `secrets.auth` file, and use Charmcraft as normal: + + ```bash + export CHARMCRAFT_AUTH=
+ ... + charmcraft upload my-super-charm.charm --release edge + ``` + +## Check currently logged in user + +To check the currently logged in user, run `charmcraft whoami`. + +> See more: {ref}`ref_commands_whoami` + +## Log out of Charmhub + +To log out of Charmhub, run `charmcraft logout`. + +> See more: {ref}`ref_commands_logout` diff --git a/docs/howto/manage-tracks.md b/docs/howto/manage-tracks.md new file mode 100644 index 000000000..d26b910a0 --- /dev/null +++ b/docs/howto/manage-tracks.md @@ -0,0 +1,101 @@ +(manage-tracks)= +# How to manage tracks + +> See also: {ref}`track` + +When you register a charm name on Charmhub, you automatically get 4 channels, all with track `latest`. However, as your charm evolves, you'll likely want to customise the shape of this track (e.g., to align with the workload) and then create new tracks in the new pattern. This document shows you how. + +(request-a-track-guardrail)= +## Request a track guardrail +> See also: {ref}`guardrail` + +To request a track guardrail, contact a Charmhub admin by creating a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . + + +(create-a-track)= +## Create a track + +Once you've requested a track guardrail, there are two ways to create a new track for your charm -- you can keep contacting a Charmhub admin every time or you can self-service. For most cases the latter option is likely to be more convenient and faster. + +### Ask a Charmhub admin + +To create a new track by contacting a Charmhub admin, create a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . The admin will create the new track that fits within the track guardrail you’ve set up for your charm. + +### Create it yourself + +To create a new track yourself, follow the steps below: + +```{important} + +As you might notice, this path is currently a little hacky. In the long-term it should become a lot smoother as there are plans to support it through the Charmcraft CLI. + +``` + +```{important} + +As you will see, this method currently relies on `charmcraft`+ `curl`. We recommend the Charmcraft bit because Charmcraft already understands the authentication mechanism used by Charmhub and can generate a suitable authentication token (macaroon) that will make it possible to then use `curl` directly to interact with the Charmhub API. This method also has the advantage that it can be adapted to use any HTTP client or library as long as it can pass custom headers. + +``` + + +**1. Enable `curl` access to the Charmhub API.** + +First, install `curl` and `jq`. + +```{important} + +You might already have both. + +``` + +Then, use Charmcraft to log in to Charmhub and export your Charmhub credentials / token (macaroon) to a file: + +```text +charmcraft login --export charmhub-creds.dat +``` + +Next, decode and extract the macaroon from the .dat file and place it in a header in an environment variable: + +```text +export CHARMHUB_MACAROON_HEADER="Authorization: Macaroon $(cat charmhub-creds.dat | base64 -d | jq -r .v)" +``` + +At this point you can use this variable in `curl` commands -- just make sure to specify the correct `Content-Type`. + +**2. Use `curl` to view the existing guardrails and tracks.** To view the guardrails and tracks associated with your charm, issue an HTTP `GET` request to `/v1//`. For example, for a charm named `hello-world-charm`: + +```text +curl https://api.charmhub.io/v1/charm/hello-world-charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" +``` + +The guardrails and tracks of the package will be under the `track-guardrails` and `tracks` keys of `metadata`. Now you know what the new track may look like. + +> See more: [Charmhub API docs > `package_metadata`](https://api.charmhub.io/docs/default.html#package_metadata) + +````{important} + +**If you want to view the guardrails and tracks for *all* published charms:** Issue an HTTP `GET` request to `/v1/`, as below: + +```text +curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" +``` + +> See more: [Charmhub API docs > `list_registered_names`](https://api.charmhub.io/docs/default.html#list_registered_names) + + +```` + + +**3. Use `curl` to create a new track.** Finally, to create a new track for your charm, issue an HTTP `POST` request to `/v1///tracks`, where `name` and `namespace` refer to the name and type of the package respectively. For example, given a charm named `hello-world-charm`, one can create two tracks `v.1` and `v.2` as follows: + +```text +curl https://api.charmhub.io/v1/charm/hello-world-charm/tracks -X POST -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -d '[{"name": "v.1"}, {"name": "v.2"}]' +``` + +Of course, the tracks must conform to the existing guardrail for the charm. + + +> See more: [Charmhub API docs > `create_tracks`](https://api.charmhub.io/docs/default.html#create_tracks) + + +That's it, you now have a new track for your charm! diff --git a/docs/howto/misc/index.md b/docs/howto/misc/index.md new file mode 100644 index 000000000..37fd765c0 --- /dev/null +++ b/docs/howto/misc/index.md @@ -0,0 +1,16 @@ +(howto-misc)= +# Miscellaneous + +```{toctree} +:maxdepth: 2 + +Migrate to `poetry` +Migrate to `python` +Cache intermediate build artefacts +Pack a hook-based charm with `charmcraft` +Pack a reactive-based charm with `charmcraft` +``` + + diff --git a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md new file mode 100644 index 000000000..82cd53096 --- /dev/null +++ b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md @@ -0,0 +1,158 @@ +(pack-a-hooks-based-charm-with-charmcraft.md)= +# How to pack a hooks-based charm with Charmcraft + +> See also: +> - {ref}`How to set up a charm project ` +> - {ref}`About charm types, by creation type ` +> - {ref}`How to turn a hooks-based charm into an ops charm ` + +You want a hooks-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. + +```{note} + + Introduced in Charmcraft 1.4 + +``` + + + + +Suppose you have a hooks-only charm, for example, [tiny-bash](https://github.com/erik78se/tiny-bash), which you can obtain as follows: + + +```text +$ git clone https://github.com/erik78se/tiny-bash +``` + + + +To make it packable by Charmcraft, all you need to do is navigate inside the charm directory and create a `charmcraft.yaml` file with the part definition for a hooks-based charm, as shown below: + + +```yaml +type: charm + +bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" + +parts: + tiny-bash: + plugin: dump + source: . + prime: + - LICENSE + - README.md + - config.yaml + - copyright + - hooks + - icon.svg + - metadata.yaml +``` + + + +After this, you can pack your charm with `charmcraft pack`, as usual: + +```text +$ charmcraft pack +Charms packed: + tiny-bash_ubuntu-20.04-amd64.charm +``` + +If successful, the result should look as below, i.e., the charm file should contain all the files listed in the `prime` section of the `tiny-bash` part and the charm manifest. + +```shell +$ unzip -l tiny-bash_ubuntu-20.04-amd64.charm +Archive: tiny-bash_ubuntu-20.04-amd64.charm + Length Date Time Name +--------- ---------- ----- ---- + 423 2021-11-12 19:37 metadata.yaml + 431 2021-11-12 19:37 README.md + 12 2021-11-12 19:37 config.yaml + 3693 2021-11-12 19:37 icon.svg + 38 2021-11-12 19:37 copyright + 261 2021-11-12 20:08 manifest.yaml + 34523 2021-11-12 19:37 LICENSE + 381 2021-11-12 19:37 hooks/update-status + 346 2021-11-12 19:37 hooks/start + 1294 2021-11-12 19:37 hooks/shared-fs-relation-changed + 563 2021-11-12 19:37 hooks/stop + 497 2021-11-12 19:37 hooks/leader-elected + 447 2021-11-12 19:37 hooks/install + 417 2021-11-12 19:37 hooks/leader-settings-changed + 811 2021-11-12 19:37 hooks/upgrade-charm + 625 2021-11-12 19:37 hooks/config-changed +--------- ------- + 44762 16 files +``` + + + +And you can also deploy your application with `juju deploy`, as usual: + +```shell +$ juju deploy ./tiny-bash_ubuntu-20.04-amd64.charm +Located local charm "tiny-bash", revision 0 +Deploying "tiny-bash" from local charm "tiny-bash", revision 0 +``` +If successful, the result should look as below, i.e., with the application status active. + +```text +$ juju status +Model Controller Cloud/Region Version SLA Timestamp +default localhost-localhost localhost/localhost 2.9.12 unsupported 17:23:23-03:00 + +App Version Status Scale Charm Store Channel Rev OS Message +tiny-bash active 1 tiny-bash local 0 ubuntu update-status ran: 20:22 + +Unit Workload Agent Machine Public address Ports Message +tiny-bash/0* active idle 0 10.2.17.31 update-status ran: 20:22 + +Machine State DNS Inst id Series AZ Message +0 started 10.2.17.31 juju-55481c-0 focal Running +``` + + + + diff --git a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md new file mode 100644 index 000000000..29b5a0071 --- /dev/null +++ b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md @@ -0,0 +1,126 @@ +(pack-a-reactive-based-charm-with-charmcraft)= +# How to pack a reactive-based charm with Charmcraft + +> See also: +> - {ref}`How to set up a charm project ` +> - {ref}`How to pack your charm using Charmcraft ` +> - {ref}`About charm types, by creation type ` + + +Suppose you want a reactive-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. + +```{note} + + Introduced in Charmcraft 1.4. + +``` +```{note} + +The reactive way to write a charm represents an old standard. The recommended way to create a charm now is using {ref}`Charmcraft ` and {ref}`Ops `. + +``` + +To pack a reactive-based charm with Charmcraft, in the charm directory create a `charmcraft.yaml` file with the part definition for a reactive-based charm: + +```yaml +type: "charm" +bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" +parts: + charm: + source: . + plugin: reactive + build-snaps: [charm] +``` + +Done. Now you can go ahead and pack your reactive-based charm with Charmcraft in the usual way using `charmcraft pack`. + + diff --git a/docs/reference/charmcraft-cli.md b/docs/reference/charmcraft-cli.md new file mode 100644 index 000000000..a5f6a3605 --- /dev/null +++ b/docs/reference/charmcraft-cli.md @@ -0,0 +1,7 @@ +(charmcraft-cli)= +# `charmcraft` CLI + +```{toctree} +:maxdepth:2 +list-of-charmcraft-cli-commands +``` From 6b4a14f67d6d63872e5f1a13b895ca065001d3ad Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Wed, 18 Dec 2024 15:17:58 -0500 Subject: [PATCH 05/19] docs: replace actions.yaml markdown with rst --- common.mk | 2 +- docs/reference/files/file-actions-yaml.md | 236 --------------------- docs/reference/files/file-actions-yaml.rst | 230 ++++++++++++++++++++ 3 files changed, 231 insertions(+), 237 deletions(-) delete mode 100644 docs/reference/files/file-actions-yaml.md create mode 100644 docs/reference/files/file-actions-yaml.rst diff --git a/common.mk b/common.mk index 4f5c9ef49..e13e3c3a6 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/reference/files/file-actions-yaml.md b/docs/reference/files/file-actions-yaml.md deleted file mode 100644 index ff8a8e13b..000000000 --- a/docs/reference/files/file-actions-yaml.md +++ /dev/null @@ -1,236 +0,0 @@ -(file-actions-yaml)= -# File `actions.yaml` - - -```{important} -Starting with Charmcraft 2.5, this file is created automatically from information you provide in {ref}`file-charmcraft-yaml`. For backwards compatibility, Charmcraft will continue to allow the use of this file, but you may not duplicate keys across the two files. - -``` - -The `actions.yaml` file in a charm project is an optional file that may be used to define the [actions](https://juju.is/docs/juju/action) supported by the charm. - -The file contains a YAML map for each defined action. Each map starts with the key ``. The rest of this document gives details about this key. - - - - - -````{dropdown} Expand to view the full spec at once - -```yaml -: - description: - parallel: - execution-group: - params: - : - : - … - -: - … -``` - -```` - - -````{dropdown} Expand to view a simple example - -The following shows a simple example of an `actions.yaml` file, defining three actions named `pause`, `resume`, and `snapshot`. The `snapshot` action takes a single string parameter named `outfile`: - -```yaml -pause: - description: Pause the database. - additionalProperties: false -resume: - description: Resume a paused database. - additionalProperties: false -snapshot: - description: | - Take a snapshot of the database. - Descriptions can be extended to multiple lines. - params: - outfile: - type: string - description: The filename to write to. - additionalProperties: false -``` - -```` - - - -````{dropdown} Expand to view a complex example - -The following example showcases a more complex configuration file that uses features of JSON schema to define detailed options. It also makes the `filename` field mandatory: - -```yaml -snapshot: - description: Take a snapshot of the database. - params: - filename: - type: string - description: The name of the snapshot file. - compression: - type: object - description: The type of compression to use. - properties: - kind: - type: string - enum: [gzip, bzip2, xz] - quality: - description: Compression quality - type: integer - minimum: 0 - maximum: 9 - required: [filename] - additionalProperties: false -``` - -The above action could be run with `juju run snapshot filename=out.tar.gz compression.kind=gzip`. This demonstrates how to pass objects with the CLI. - -```` - - - -## `` - -**Status:** - -Required, one for each action. - -**Purpose: To define an action supported by the charm. The information stated here will feed into `juju actions ` and `juju run `, helping a Juju end user know what actions and action parameters are defined for the charm. -> See more: [Juju | `juju actions`](https://juju.is/docs/juju/juju-actions), [Juju | `juju run`](https://juju.is/docs/juju/juju-run) - -**Structure:** *Name:* The name of the key (``) is defined by the charm author. It must be -a valid Python [identifier](https://docs.python.org/3/reference/lexical_analysis.html#identifiers) that does not collide with Python [keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords) except that it may contain hyphens (which will be mapped to underscores in the Python event handler). *Type:* Map. *Value:* A series of keys-value pairs corresponding to action metadata and to parameter validation, defined as follows: - -```yaml -: - # Action metadata keys - description: - parallel: - execution-group: - # Parameter validation keys, cf. JSON Schema object - params: - : <...> - : <...> - … - -``` - -```{important} - -As you can see, the action definition schema defines a typical JSON Schema object, except: -1. It includes some new keys specific to actions: `description`, `parallel`, and `execution-group`. -2. It does not currently support the JSON Schema concepts `$schema` and `$ref`. -3. The `additionalProperties` and `required` keys from JSON Schema can be used at the top-level of an action (adjacent to `description` and `params`), but also used anywhere within a nested schema. - -> See more: [JSON schema](https://www.learnjsonschema.com/) - -``` - -## `.description` - -**Status:** Optional but recommended. - -**Purpose:** To describe the action. - -**Structure:** *Type:* String. - - -## `.execution-group` - -**Status:** Optional, defaults to “”. - -**Purpose:** Sets in which execution group to place tasks created by this action. - -**Structure:** *Type:* String. - -> See more: [Juju | `juju run`](https://juju.is/docs/juju/juju-run), [Juju | Task](https://juju.is/docs/juju/task) - -## `.parallel` - -**Status:** Optional, defaults to false. - -**Purpose:** To set whether to allow tasks created by this action to execute in parallel. - -**Structure:** *Type:* Boolean. - -> See more: [Juju | `juju run`](https://juju.is/docs/juju/juju-run), [Juju | Task](https://juju.is/docs/juju/task) - -## `.params` - -**Status:** Optional. - -**Purpose:** To define the fixed parameters for the action. Fixed parameters are those with a name given by a fixed string. - -**Structure:** *Type:* Map. *Value:* One or more key-value pairs where each key is a parameter name and each value is the YAML equivalent of a valid [JSON Schema](https://json-schema.org/). The entire map of `.params` is inserted into the action schema object as a “properties” validation keyword. The Juju CLI may read the “description” annotation keyword of each parameter to present to the user when describing the action. - - -## `.*` - -**Status:** Optional. - -**Purpose:** To define additional validation or annotation keywords of the action schema object. - -**Structure:** *Name:* A valid keyword of a JSON Schema object instance that will be merged into the action schema object. For example, `additionalProperties` or `required`. *Type:* Various. - - - - -> Contributors: @charlie4284 , @dannycocks , @mmkay, @tmihoc diff --git a/docs/reference/files/file-actions-yaml.rst b/docs/reference/files/file-actions-yaml.rst new file mode 100644 index 000000000..04c13c360 --- /dev/null +++ b/docs/reference/files/file-actions-yaml.rst @@ -0,0 +1,230 @@ +.. _file-actions-yaml: + +``actions.yaml`` +**************** + +.. important:: + Starting with Charmcraft 2.5, this file is created automatically from information + you provide in :ref:`charmcraft.yaml`. For backwards + compatibility, Charmcraft will continue to allow the use of this file, but you may + not duplicate keys across the two files. + + +The ``actions.yaml`` file in a charm project is an optional file that may be used to +define the `actions`_ supported by the charm. + +The file contains a YAML map for each defined action. Each map starts with an +```` key. The rest of this document gives details about this key. + + +.. dropdown:: Expand to view the full spec at once + + .. code:: yaml + + : + description: + parallel: + execution-group: + params: + : + : + … + + : + … + + +.. dropdown:: Expand to view a simple example + + The following shows a simple example of an ``actions.yaml`` file, defining three + actions named ``pause``, ``resume``, and ``snapshot``. The ``snapshot`` action takes + a single string parameter named ``outfile``: + + .. code:: yaml + + pause: + description: Pause the database. + additionalProperties: false + resume: + description: Resume a paused database. + additionalProperties: false + snapshot: + description: | + Take a snapshot of the database. + Descriptions can be extended to multiple lines. + params: + outfile: + type: string + description: The filename to write to. + additionalProperties: false + +.. dropdown:: Expand to view a complex example + + The following example showcases a more complex configuration file that uses + features of JSON schema to define detailed options. It also makes the + ``filename`` field mandatory: + + .. code:: yaml + + snapshot: + description: Take a snapshot of the database. + params: + filename: + type: string + description: The name of the snapshot file. + compression: + type: object + description: The type of compression to use. + properties: + kind: + type: string + enum: [gzip, bzip2, xz] + quality: + description: Compression quality + type: integer + minimum: 0 + maximum: 9 + required: [filename] + additionalProperties: false + + The above action could be run with + ``juju run snapshot filename=out.tar.gz compression.kind=gzip``. + This demonstrates how to pass objects with the CLI. + +```` +============ + +**Status:** Required, one for each action. + +**Purpose:** To define an action supported by the charm. The information stated here +will feed into ``juju actions `` and ``juju run ``, +helping a Juju end user know what actions and action parameters are defined for the +charm. + + See more: + `Juju \| juju actions `_, + `Juju \| juju run `_ + +**Structure:** + +*Name:* The name of the key (````) is defined by the charm +author. It must be a valid Python +`identifier `_ +that does not collide with Python +`keywords `_ +except that it may contain hyphens (which will be mapped to underscores in the Python +event handler). + +*Type:* Map. + +*Value:* A series of keys-value pairs corresponding to action metadata and to parameter +validation, defined as follows: + +.. code:: yaml + + : + # Action metadata keys + description: + parallel: + execution-group: + # Parameter validation keys, cf. JSON Schema object + params: + : <...> + : <...> + … + + +.. important:: + + As you can see, the action definition schema defines a typical JSON Schema object, + except: + + 1. It includes some new keys specific to actions: ``description``, ``parallel``, + and ``execution-group``. + 2. It does not currently support the JSON Schema concepts ``$schema`` and ``$ref``. + 3. The ``additionalProperties`` and ``required`` keys from JSON Schema can be used + at the top-level of an action (adjacent to ``description`` and ``params``), but + also used anywhere within a nested schema. + + See more: `JSON schema `_ + +``.description`` +------------------------ + +**Status:** Optional but recommended. + +**Purpose:** To describe the action. + +**Structure:** *Type:* String. + + +``.execution-group`` +---------------------------- + +**Status:** Optional, defaults to ``""`` (empty string). + +**Purpose:** Sets in which execution group to place tasks created by this action. + +**Structure:** *Type:* String. + + See more: `Juju | juju run `_, `Juju | Task `_ + +``.parallel`` +--------------------- + +**Status:** Optional, defaults to false. + +**Purpose:** Sets whether to allow tasks created by this action to execute in parallel. + +**Structure:** *Type:* Boolean. + + See more: `Juju | juju run `_, `Juju | Task `_ + +``.params`` +------------------- + +**Status:** Optional. + +**Purpose:** To define the fixed parameters for the action. Fixed parameters are those +with a name given by a fixed string. + +**Structure:** + +*Type:* Map. + +*Value:* One or more key-value pairs where each key is a parameter name and each value +is the YAML equivalent of a valid `JSON Schema`_. The entire +map of ``.params`` is inserted into the action schema object as a “properties” +validation keyword. The Juju CLI may read the “description” annotation keyword of each +parameter to present to the user when describing the action. + +``.*`` +-------------- + +**Status:** Optional. + +**Purpose:** To define additional validation or annotation keywords of the action +schema object. + +**Structure:** + +*Name:* A valid keyword of a `JSON Schema`_ object instance that will be merged into the +action schema object. For example, ``additionalProperties`` or ``required``. + +*Type:* Various. + +Juju will parse additional keywords as a `JSON Schema`_ with some limitations: + +- The ``$schema`` and ``$ref`` keys are prohibited +- `params `_ is treated as a single top-level JSON Schema instance of + type `object `_ with a map of ``properties`` corresponding to + each key in ``params``. This instance is what Juju uses to validate user input. + +It is highly recommended to provide ``additionalProperties: false`` to avoid user +frustration with accidental typos. + +.. _actions: https://juju.is/docs/juju/action +.. _run: https://juju.is/docs/juju/juju-run +.. _task: https://juju.is/docs/juju/task +.. _JSON-Schema: https://json-schema.org/ +.. _jsonschema-object: https://json-schema.org/understanding-json-schema/reference/object.html From 5e30dc34de743f1ec3c17db2873367f07b721f81 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 08:43:17 -0500 Subject: [PATCH 06/19] docs: move charmcraft.yaml file reference to rst --- .../files/charmcraft-sample-charm.yaml | 173 ++++ docs/reference/files/file-charmcraft-yaml.rst | 901 ++++++++++++++++++ 2 files changed, 1074 insertions(+) create mode 100644 docs/reference/files/charmcraft-sample-charm.yaml create mode 100644 docs/reference/files/file-charmcraft-yaml.rst diff --git a/docs/reference/files/charmcraft-sample-charm.yaml b/docs/reference/files/charmcraft-sample-charm.yaml new file mode 100644 index 000000000..4f4f38a40 --- /dev/null +++ b/docs/reference/files/charmcraft-sample-charm.yaml @@ -0,0 +1,173 @@ +actions: + snapshot: + description: Take a snapshot of the database. + params: + filename: + type: string + description: The name of the snapshot file. + compression: + type: object + description: The type of compression to use. + properties: + kind: + type: string + enum: + - gzip + - bzip2 + - xz + quality: + description: Compression quality + type: integer + minimum: 0 + maximum: 9 + required: + - my-favourite-photo.tiff + additionalProperties: false +analysis: + ignore: + attributes: + - framework + linters: + - entrypoint +assumes: + - any-of: + - juju >= 2.9.23 + - all-of: + - juju >= 3.5 + - juju < 4.0 + - k8s-api +base: ubuntu@24.04 +build-base: ubuntu@devel +charm-libs: + - lib: postgresql.postgres_client + version: "1" + - lib: mysql.mysql + version: "0.5" +config: + options: + name: + default: Wiki + description: The name, or Title of the Wiki + type: string + logo: + description: URL to fetch logo from + type: string + admins: + description: 'Comma-separated list of admin users to create: user:pass[,user:pass]+' + type: string + debug: + default: false + type: boolean + description: turn on debugging features of mediawiki + port: + default: 80 + type: int + description: port on which to serve the wiki + timeout: + default: 60.0 + type: float + description: maximum time before rendering a page will fail + certificate: + type: secret + description: TLS certificate to use for securing connections +containers: + super-app: + resource: super-app-image + mounts: + - storage: logs + location: /logs +description: | + This is a long description of the charm. It gets used when the charm is uploaded to + Charmhub. It is a string, but can easily be made a multiline string or any other + YAML string format. +devices: + super-cool-gpu: + type: amd.com/gpu + description: Some sweet AMD GPU + countmin: 69 + countmax: 420 + lame-gpu: + type: nvidia.com/gpu + description: A GPU I regret buying + countmin: 0 + countmax: 1 +extra-bindings: + Ring of Power: +links: + contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont + documentation: https://juju.is/docs/sdk/charmcraft-yaml + issues: + - https://launchpad.net/~charmcraft-team + - https://github.com/canonical/charmcraft/issues + source: + - https://github.com/canonical/charmcraft + website: + - https://snapcraft.io/charmcraft +name: full-charm +parts: + my-charm: + plugin: charm + source: . + charm-entrypoint: src/charm.py + charm-binary-python-packages: [] + charm-python-packages: [] + charm-requirements: [requirements.txt] + charm-strict-dependencies: false + another-part: + plugin: nil + source: +peers: + friend: + interface: life + limit: 150 + optional: true + scope: container +platforms: + all: + build-on: + - amd64 + - arm64 + - riscv64 + - s390x + - ppc64el + - armhf + build-for: [all] + amd64: + build-on: [amd64] + build-for: [amd64] + arm64: + build-on: + - amd64 + - arm64 + build-for: + - arm64 +provides: + self: + interface: identity +requires: + parent: + interface: birth + limit: 2 + optional: false + scope: global +resources: + water: + type: file + filename: /dev/h2o + super-app-image: + type: oci-image + description: OCI image for the Super App (hub.docker.com/_/super-app) +storage: # Possible storage for the charm + jbod: + type: block + description: A nice block storage for me to use as swap space + shared: false + properties: + - transient +subordinate: false +summary: A fully-defined charm, making use of all the available charm keywords. +terms: + - Butterscotch is regal + - Cara is adorable +title: My awesome charm +type: charm diff --git a/docs/reference/files/file-charmcraft-yaml.rst b/docs/reference/files/file-charmcraft-yaml.rst new file mode 100644 index 000000000..4445a50f3 --- /dev/null +++ b/docs/reference/files/file-charmcraft-yaml.rst @@ -0,0 +1,901 @@ +.. _file-charmcraft-yaml: +.. highlight:: yaml + +``charmcraft.yaml`` +******************* + +.. + AUTHOR NOTE: + The full list of keys is defined in the Charmcraft project (but this implies upstream keys from craft-application): + https://github.com/canonical/charmcraft/blob/3.2.0/charmcraft/models/project.py#L381-L1070 + + Technically, the only key required upfront is type. But then, depending on what you choose, other keys become required as well. (The required keys are the ones that are set to a value, unless that value is a pydantic.Field that doesn't have either a default or a default_factory parameter) + + The parts key connects to an external library. The plugin properties are defined here: https://canonical-craft-parts.readthedocs-hosted.com/en/latest/reference/part_properties.html + + This test file shows the full spec at once: https://github.com/canonical/charmcraft/blob/main/tests/unit/models/valid_charms_yaml/full.yaml + +.. important:: + ``charmcraft.yaml`` file is the only ``yaml`` file generated by ``charmcraft init`` and the only ``yaml`` file in a charm project that a charm author should edit directly. + + Charmcraft will use the information you provide here to generate :ref:`file-actions-yaml`, :ref:`file-config-yaml`, and :ref:`file-metadata-yaml`, as well as all the other files it usually does. + +``charmcraft.yaml`` is a file in your charm project that contains keys that allow you to declare information about the project in a form that can be used by Charmcraft. + +.. note:: + If you're starting from an empty file, the only required key is the ``type`` key. However, depending on what value you set it to (``charm`` or ``bundle``), other keys become required as well. + + +.. dropdown:: Expand to view a full charm with sample content all at once + + .. literalinclude:: charmcraft-sample-charm.yaml + +.. _file-charmcraft-yaml-actions:: +``actions`` +=========== + + Definition owned by `Juju`_. Used by `Charmcraft`_, `Charmhub`_, `Juju`_. + +**Status:** Optional. + +**Purpose:** Defines actions the charm can take. + +**Name:** String = user-defined action name. + +**Value:** Mapping. + +The value of this key is the contents of :ref:`file-actions-yaml`. + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: actions: + :end-before: analysis: + + +.. _file-charmcraft-yaml-analysis:: +``analysis`` +============ + + Definition owned by `Charmcraft`_. Used by `Charmcraft`_. + +**Status:** Optional. + +**Purpose:** Defines how the analysis done on the package will behave. This analysis is run implicitly as part of the ``pack`` command but can be called explicitly with the ``charmcraft analyse`` command. + +**Structure:** + +.. code:: + + analysis: + ignore: + attributes: [,...] + linters: [,...] + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: analysis: + :end-before: assumes: + +.. _file-charmcraft-yaml-assumes:: +``assumes`` +=========== + +**Status:** Optional. Recommended for Kubernetes charms. + +**Purpose:** Allows charm authors to explicitly state in the metadata of a charm various features that a Juju model must be able to provide to ensure that the charm can be successfully deployed on it. When a charm comes preloaded with such requirements, this enables Juju to perform a pre-deployment check and to display user-friendly error messages if a feature requirement cannot be met by the model that the user is trying to deploy the charm to. If the assumes section of the charm metadata is omitted, Juju will make a best-effort attempt to deploy the charm, and users must rely on the output of `juju status` to figure out whether the deployment was successful. The `assumes` key is available since 2.9.23. + +**Structure:** The key consists of a list of features that can be given either directly or, depending on the complexity of the condition you want to enforce, nested under one or both of the boolean expressions ``any-of`` or ``all-of``, as shown below. In order for a charm to be deployed, all entries in the ``assumes` block must be satisfied. + +.. code:: + + assumes: + - + - any-of: + - + - + - all-of: + - + - + +.. This should really go into the Juju docs, as they are the ones who parse it. + +.. list-table:: Supported features + + * - Structure + - Description + - Examples + - Juju versions + * - ``juju `` + - The charm deploys iff the model runs agent binaries with the specified Juju version(s). + - ``juju >= 3.0`` + ``juju < 4.0`` + - Since 2.9.23 + * - ``k8s-api`` + - The charm deploys iff the `backing cloud `_ for the model is Kubernetes. + - ``k8s-api`` + - Since Juju 2.9.23 + +.. dropdown:: Simple example + + .. code:: + + assumes: + - juju >= 2.9.23 + - k8s-api + +.. dropdown:: Complex example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: assumes: + :end-before: base: + +.. _file-charmcraft-yaml-base:: +``base`` +======== + +**Status:** Required in most cases if :ref:`type` is ``charm``. + +**Purpose:** Specifies the operating system on which the charm will build and run. + +**Structure:** + +.. code:: + + base: @ + +**Example:** + +.. code:: + + base: ubuntu@24.04 + + +.. _file-charmcraft-yaml-bases: +``bases`` +========= + +.. note:: + + ``bases`` is deprecated, replaced by `base`_, `build-base`_, and `platforms`_. + + .. dropdown:: See more + + The ``bases`` key is only accepted for bases supported before 2024-01-01. + + .. code:: + + # The run time base, the base format is @, + # accepted bases are: + # - ubuntu@22.04 + # - ubuntu@24.04 + base: + # The build time base. Only used if the runtime base is not stable. + # Accepts all runtime bases and ``ubuntu@devel`` + build-base: + + # The supported platforms + platforms: + : + build-on: | + build-for: | + +**Status:** Deprecated. Conflicts with the `base`_, `build-base`_, and `platforms`_ keys. Not allowed if `type`_ is ``bundle``. + +**Purpose:** Specifies a list of environments (OS version and architecture) where the charm must be built on and run on. + +**Structure:** This key supports a list of bases where the charm can be built, and where that build can run. Each item can be expressed using two different internal structures, a short and a long form. The long one is more explicit: + +.. code:: + + bases: + - build-on: + - name: + channel: + architectures: + - + run-on: + - name: + channel: + architectures: + - + +The ``run-on`` part of each entry is optional and defaults to what's specified in the corresponding ``build-on``. In both structures the list of architecture strings is also optional, defaulting to the architecture of the current machine. + +The short form is more concise and simple (at the cost of being less flexible): + +.. code:: + + bases: + - name: + channel: + architectures: + - + +It implies that the specified base is to be used for both ``build-on`` and ``run-on``. As above, the list of architecture strings is also optional, defaulting to the machine architecture. + + +.. dropdown:: Example + + .. code:: + + bases: + - build-on: + - name: ubuntu + channel: '22.04' + architectures: + - amd64 + - riscv64 + - name: ubuntu + channel: '20.04' + architectures: + - amd64 + - arm64 + run-on: + - name: ubuntu + channel: '22.04' + architectures: + - amd64 + - name: ubuntu + channel: '22.04' + architectures: + - riscv64 + - name: ubuntu + channel: '22.04' + architectures: + - arm64 + - build-on: + - name: ubuntu + channel: '24.04' + run-on: + - name: ubuntu + channel: '24.04' + architectures: + - amd64 + - arm64 + - riscv64 + - s390x + - ppc64el + - armhf + +.. _file-charmcraft-yaml-build-base: +``build-base`` +============== + +**Status:** Only valid if `base`_ is a development base. + +**Purpose:** Specifies the operating system on which the charm will be built. + +**Structure:** + +.. code:: + + build-base: @ + +**Example:** + +.. code:: + + base: ubuntu@devel + +.. _file-charmcraft-yaml-charm-libs: +``charm-libs`` +============== + +**Status:** Optional. + +**Purpose:** Declares charm libraries for Charmcraft to include in the charm project. For each lib, include both the lib name (in ``.`` format) and the lib version (in ``"[.]"`` string format). + +**Structure:** + +.. code:: + + charm-libs: + - lib: . + version: "[.]" # Must be a string, not a number. + +**Example:** + +.. literalinclude:: charmcraft-sample-charm.yaml + :start-at: charm-libs: + :end-before: config: + +.. _file-charmcraft-yaml-charmhub: +``charmhub`` +===== + + +.. warning:: + + This key is only meaningful in Charmcraft 2. Use the environment variables ``CHARMCRAFT_STORE_API_URL``, ``CHARMCRAFT_UPLOAD_URL`` and ``CHARMCRAFT_REGISTRY_URL`` for newer versions of Charmcraft. + + +**Status:** Deprecated and nonfunctional in Charmcraft 3. + +**Purpose:** Configures Charmcraft's interaction with store servers. + +**Structure:** This key allows for the configuration of three values---the base URL for the Charmhub API, the base URL to push binaries to Charmhub and the URL of the container registry for OCI image uploads. These keys are also optional. + +.. code:: + + charmhub: + api-url: + storage-url: + registry-url: + +The key is used mostly in the context of "private" charm stores, defaulting to the standard Canonical services to operate with charms. + +.. dropdown:: Example + + .. code:: + + charmhub: + api-url: https://api.staging.charmhub.io + storage-url: https://storage.staging.snapcraftcontent.com + +.. _file-charmcraft-yaml-config: +``config`` +========= + + See first: `Juju | Application configuration `_ + +**Status:** Optional. + +**Purpose:** Creates user-facing configuration options for the charm. + +**Structure:** + +.. code:: + + config: + options: + # Each option name is the name by which the charm will query the option. + + # (Optional): A string describing the option. Also appears on charmhub.io + description: + +If ``type`` is ``secret``, this is a string that needs to correspond to the secret URI. + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: config: + :end-before: containers: + +.. _file-charmcraft-yaml-containers: +``containers`` +============== + +**Status:** Required for Kubernetes charms (except for proxy charms running on Kubernetes). + +**Purpose:** The ``containers`` key allows you to define a map of containers to be created adjacent to the charm (as a sidecar, in the same pod). + +**Structure:** This key consists of a list of containers along with their specification. Each container can be specified in terms of ``resource``, ``bases``, ``uid``, ``gid`` and ``mounts``, where one of either the ``resource`` or the ``bases`` subkeys must be defined, and ``mounts`` is optional. ``resource`` stands for the OCI image resource used to create the container; to use it, specify an OCI image resource name (that you will then define further in the `resources`_ block). ``bases`` is a list of bases to be used for resolving a container image, in descending order of preference; to use it, specify a base name (for example, ``ubuntu``, ``centos``, ``windows``, ``osx``, ``opensuse``), a `channel `_, and an architecture. ``mounts`` is a list of mounted storages for this container; to use it, specify the name of the storage to mount from the charm storage and, optionally, the location where to mount the storage. Starting with Juju 3.5.0, ``uid`` and ``gid`` are the UID and, respectively, GID to run the Pebble entry process for this container as; they can be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved for users) and the default is 0 (root). + +.. code:: + + containers: + : + resource: + bases: + - name: + channel: + architectures: + - + mounts: + - storage: + location: + uid: + gid: + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: containers: + :end-before: description: + +.. _file-charmcraft-yaml-description: +``description`` +=============== + +**Status:** Required if the `type`_ key is set to `charm`. Recommended otherwise. + +**Example:** + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: description: | + :end-before: devices: + +.. _file-charmcraft-yaml-devices: +``devices`` +=========== + +**Status:** Optional + +**Purpose:** Defines the device requests for the charm, for example a GPU. + +**Structure:** + +.. code:: + + devices: + # Each key represents the name of the device + : + # (Required) The type of device requested + type: gpu | nvidia.com/gpu | amd.com/gpu + # (Optional) Description of the requested device + description: + # (Optional) Minimum number of devices required + countmin: + # (Optional) Maximum number of devices required + countmax: + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: devices: + :end-before: extra-bindings: + +.. _file-charmcraft-yaml-extra-bindings: +``extra-bindings`` +================== + +**Status:** Optional. + +**Purpose:** Extra bindings for the charm. For example binding extra network interfaces. + +**Structure:** A key-only map; key represents the name of the binding: + +.. code:: + + extra-bindings: + : + +**Example:** + +.. literalinclude:: charmcraft-sample-charm.yaml + :start-at: extra-bindings: + :end-before: links: + +.. _file-charmcraft-yaml-links: +``links`` +========= + +**Status:** Optional. Recommended. + +**Purpose:** Links to various additional information, to be displayed on Charmhub. + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: links: + :end-before: name: + +.. _file-charmcraft-yaml-name: +``name`` +======== + +**Status:** Required if the `type`_ key is set to ``charm``. + +**Purpose:** The name of the charm. Determines the ``.charm`` file name and, if the charm is published on Charmhub, the charm page URL in Charmhub. As a result, it also determines the name administrators will ultimately use to deploy the charm. E.g. ``juju deploy ``. + +**Structure:** + +.. code:: + + name: + +**Example:** + +.. literalinclude:: charmcraft-sample-charm.yaml + :start-at: name: + :end-before: parts: + +.. file-charmcraft-yaml-parts: +``parts`` +========= + +**Status:** Recommended. + +**Purpose:** Configures the various mechanisms to obtain, process and prepare data from different sources that end up being a part of the final charm. + +**Structure:** Mapping. Keys are user-defined part names. The value of each key is a map where keys are part properties. + + See more: :ref:`part_properties` + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: parts: + :end-before: peers: + +.. dropdown:: Details + + .. TODO: These should be moved to their own plugin pages. + + Charmcraft offers three custom parts plugins specifically for writing charms: + + **The** ``charm`` **plugin** + + Used to pack a Charm that is based on the `Operator framework`_. + + Supports the following configuration: + + ..code:: + + parts: + my-charm: + plugin: charm + charm-entrypoint: + charm-requirements: + charm-python-packages: + charm-binary-python-packages: + prime: + + In detail: + + - ``charm-entrypoint``: The charm entry point, relative to the project directory. It is optional if not defined defaults to ``src/charm.py``. + - ``charm-requirements``: A list of requirements files specifying Python dependencies. It is optional; if not defined, defaults to a list with one ``requirements.txt`` entry if that file is present in the project directory. + - ``charm-python-packages``: A list of Python packages to install before installing requirements. These packages will be installed from sources and built locally at packing time. It is optional, defaults to empty. + - ``charm-binary-python-packages``: A list of python packages to install before installing requirements and regular Python packages. Binary packages are allowed, but they may also be installed from sources if a package is only available in source form. It is optional, defaults to empty. + + + **The** ``bundle`` **plugin** + + Used to pack a `charm bundle `_, a collection of charms which have been carefully combined and configured in order to automate a multi-charm solution. + + Supports the following configuration: + + .. code:: + + parts: + my-bundle: + plugin: bundle + + **The** ``reactive`` **plugin** + + Used to pack charms using the reactive framework. + + Note that this is a framework that has now been superseded by the `Operator framework`_. Please use that framework instead of reactive. Support for reactive in Charmcraft is only to ease the transition of old charms into the new framework. + + Supports the following configuration: + + .. code:: + + parts: + charm: + source: . + plugin: reactive + build-snaps: [charm] + reactive-charm-build-arguments: + + The ``reactive_charm_build_arguments`` allows to include extra command line arguments in the underlying ``charm build`` call. + + +``peers``, ``provides``, ``requires`` +===================================== + + See also: `Juju | Relation (integration) `_ + +.. dropdown:: Expand to view an example featuring all three relation keys (peers, provides, or requires) + + .. code:: + + peers: + friend: + interface: life + limit: 150 + optional: true + scope: container + provides: + self: + interface: identity + requires: + parent: + interface: birth + limit: 2 + optional: false + scope: global + + +.. dropdown:: Expand to view the full schema for a chosen endpoint role (peers, provides, or requires) + + ..code:: + + : # 'peers', 'provides', or 'requires' + # Each key represents the name of the endpoint as known by this charm + : + # (Required) The interface schema that this relation conforms to + interface: + + # (Optional) Maximum number of supported connections to this relation + # endpoint. This field is an integer + limit: + + # (Optional) Defines if the relation is required. Informational only. + optional: true | false + + # (Optional) The scope of the relation. Defaults to "global" + scope: global | container + +```` +------------------- + +**Status:** If you want to define any kind of integration, required. + +**Purpose:** To define an integration endpoint. + +**Structure:** + +*Name:* Depending on what kind of an integration you are trying to define: ``peers``, ``provides``, or ``requires``. + +*Type:* Map. + +*Value:* One or more key-value pairs denoting a relation and its associated properties. + +``.`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Status:** Required. + +**Purpose:** To define the name of the relation as known by this charm. + +**Structure:** + +*Name: User-defined.* + +*Type:* string. + +``..interface`` +````````````````````````````````````````````` + +**Status:** Required. + +**Purpose:** To define the interface schema that this relation conforms to. + +**Structure:** + +*Type:* String. + +*Value:* The name of the interface. Usually defined by the author of the charm providing the interface. Cannot be ``juju``. Cannot begin with ``juju-``. Must only contain characters `a-z` and `-` and cannot start with `-`. + +.. warning:: The interface name is the only means of establishing whether two charms are compatible for integration; and carries with it nothing more than a mutual promise that the provider and requirer know the communication protocol implied by the name. + +``..limit`` +````````````````````````````````````````` + +**Status:** Optional. + +**Purpose:** To define the maximum number of supported connections to this relation endpoint. + +**Structure:** + +*Type:* Integer. + +``..optional`` +```````````````````````````````````````````` + +**Status:** Optional. + +**Purpose:** To define if the relation is required. Informational only. + +**Structure:** + +*Type:* Boolean. + +*Default value:* ``false`` + +``..scope`` +````````````````````````````````````````` + +**Status:** Optional. + +**Purpose:** To define the scope of the relation, that is, the set of units from integrated applications that are reported to the unit as members of the integration. + +**Structure:** + +*Type:* String. + +**Possible values:** ``container``, ``global``. + +Container-scoped integrations are restricted to reporting details of a single principal unit to a single subordinate, and vice versa, while global integrations consider all possible remote units. Subordinate charms are only valid if they have at least one ``requires`` integration with ``container`` scope. + +**Default value:** ``global``. + + +.. _file-charmcraft-yaml-resources: +``resources`` +===== + + See first: `Juju | Charm resource `_ + +**Status:** Optional. + +**Purpose:** To define a `resource `_ for your charm. + +.. note:: Kubernetes charms must declare an ``oci-image`` resource for each container they define in the `containers`_ mapping. + +**Structure:** + +.. code:: + + resources: + : + type: or + description: + filename: + +.. dropdown:: File resource example + + + .. code:: + + resources: + water: + type: file + filename: /dev/h2o + +.. dropdown:: OCI image example + + .. code:: + + resources: + super-app-image: + type: oci-image + description: OCI image for the Super App (hub.docker.com/_/super-app) + + +.. _file-charmcraft-yaml-storage: +``storage`` +=========== + +**Status:** Optional. + +**Purpose:** Storage requests for the charm. + +**Structure:** + +.. code:: + + storage: + # Each key represents the name of the storage + : + + # (Required) Type of the requested storage + # The filesystem type requests a directory in which the charm may store files. + # If the storage provider only supports provisioning disks, then a disk will be + # created, attached, partitiioned, and a filesystem created on top, and the + # filesystem will be presented to the charm as normal. + # The block type requests a raw block device, typically disks or logical volumes. + type: filesystem | block + + # (Optional) Description of the storage requested + description: + + # (Optional) The mount location for filesystem stores. For multi-stores + # the location acts as the parent directory for each mounted store. + location: + + # (Optional) Indicates if all units of the application share the storage + shared: true | false + + # (Optional) Indicates if the storage should be made read-only (where possible) + read-only: true | false + + # (Optional) The number of storage instances to be requested + multiple: + range: | - | - | + + + # (Optional) Minimum size of requested storage in forms G, GiB, GB. Size + # multipliers are M, G, T, P, E, Z or Y. With no multiplier supplied, M + # is implied. + minimum-size: | + + # (Optional) List of properties, only supported value is "transient" + properties: + - transient + + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: storage: # Possible storage for the charm + :end-before: subordinate: + +.. _file-charmcraft-yaml-subordinate: +``subordinate`` +=============== + +**Status:** Optional. + +**Purpose:** Configures whether the charm is meant to be deployed as a subordinate to a principal charm. + +**Structure:** + +.. code:: + + subordinate: true | false + +**Example:** + +.. code:: + + subordinate: false + +.. _file-charmcraft-yaml-summary: +``summary`` +=========== + +**Status:** Required if the `type`_ key is set to ``charm``. + +**Structure:** A short, one-line description of the charm. No more than 78 characters. + +.. dropdown:: Example + + .. code:: + + summary: A Juju charm to run a Traefik-powered ingress controller on Kubernetes. + +.. _file-charmcraft-yaml-terms: +``terms`` +========= + +**Status:** Optional. + +**Purpose:** Lists the terms that any charm user agree to when they're using the charm. + +**Structure:** The list of terms: + +.. code:: + + terms: + - + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: terms: + :end-before: title: My awesome charm + +.. _file-charmcraft-yaml-title: +``title`` +========= + +**Status:** Optional, but recommended + +**Purpose:** A human-readable name for your charm + +.. dropdown:: Example + + .. literalinclude:: charmcraft-sample-charm.yaml + :start-at: title: + :end-before: type: + +.. _file-charmcraft-yaml-type: +``type`` +======== + +**Status:** Required. + +**Purpose:** Indicates whether charmcraft will pack a charm or a bundle. + +**Structure:** + +*Type:* String. + +*Value:* ``charm`` or ``bundle``. + +.. dropdown:: Example + + .. code:: + + type: charm + +.. _juju-bundles: https://juju.is/docs/olm/bundles +.. _juju-configuration: https://juju.is/docs/juju/configuration +.. _juju-resource: https://juju.is/docs/juju/charm-resource From 1db6b04a74780ca66c4b62e37d283e48f6a83d88 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 08:45:27 -0500 Subject: [PATCH 07/19] docs: update links --- docs/reuse/links.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reuse/links.txt b/docs/reuse/links.txt index c6cf96137..0e6be7ae9 100644 --- a/docs/reuse/links.txt +++ b/docs/reuse/links.txt @@ -1,8 +1,10 @@ .. _Charmcraft: https://canonical-charmcraft.readthedocs-hosted.com +.. _Charmhub: https://charmhub.io .. _Chisel: https://github.com/canonical/chisel .. _`Chisel releases`: https://github.com/canonical/chisel-releases .. _`Craft-parts`: https://canonical-craft-parts.readthedocs-hosted.com .. _Docker: https://docs.docker.com/ +.. _Juju: https://juju.is/docs .. _`feature request`: https://github.com/canonical/charmcraft/issues/new?assignees=&labels=Enhancement&projects=&template=task.yaml .. _`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 From e60edb9676ea9c37979100cc68fdc1a53e52755c Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 08:52:16 -0500 Subject: [PATCH 08/19] docs: swap config.yaml reference to RST --- docs/reference/files/file-config-yaml.md | 80 ----------------------- docs/reference/files/file-config-yaml.rst | 66 +++++++++++++++++++ 2 files changed, 66 insertions(+), 80 deletions(-) delete mode 100644 docs/reference/files/file-config-yaml.md create mode 100644 docs/reference/files/file-config-yaml.rst diff --git a/docs/reference/files/file-config-yaml.md b/docs/reference/files/file-config-yaml.md deleted file mode 100644 index 6d3b493a0..000000000 --- a/docs/reference/files/file-config-yaml.md +++ /dev/null @@ -1,80 +0,0 @@ -(file-config-yaml)= -# File 'config.yaml' - -```{important} -Starting with Charmcraft 2.5, this file is created automatically from information you provide in {ref}`file-charmcraft-yaml`. For backwards compatibility, Charmcraft will continue to allow the use of this file, but you may not duplicate keys across the two files. - -``` - -The`config.yaml` in a charm's root directory is an optional file that may be used to define the configuration options supported by a charm. - -The definitions are collected under a single YAML map called `options`. The rest of this doc gives details about this map. - -## `options` - -**Status:** Optional. - -**Purpose:** The `options` key allows charm authors to declare the configuration options that they have defined for a charm. - -**Structure:** The key contains a definition block for each option, where each definition consists of a charm-author-defined option name and an option description, given in 3 fields -- type, description, and default value: - -```yaml -options: -

`applications.`

- -**Purpose:** Holds your application definition. - -**Name:** The name of the application. User-defined, usually identical to [`applications..charm`](#heading--applications-application-charm) - -

`applications..annotations`

- -```text - # - # annotations: - # - # Affects the GUI only. It provides horizontal and vertical placement of - # the application's icon on the GUI's canvas. Annotations are expressed in - # terms of 'x' and 'y' coordinates. - # - - annotations: - gui-x: 450 - gui-y: 550 - -``` - -## `applications..base` - -## `applications..bindings` - -```text - # - # bindings: - # - # Maps endpoints to network spaces. Used to constrain relations to specific - # subnets in environments where machines have multiple network devices. - # The empty ("") key represents all endpoints and can be used to specify the - # default space for any endpoint that is not explicitly bound to a space. - - bindings: - "": alpha - kube-api-endpoint: internal - loadbalancer: dmz - -``` - -## `applications..channel` - -**Purpose:** States what the preferred channel should be used when deploying a non-local charm. **Note:** Charmhub charms expect `//` format (e.g., `latest/stable`). - -**Example:** - -```text -channel: latest/edge -``` - -## `applications..charm` - -**Purpose:** States what charm to use for the application. **If you're defining a public bundle:** Use a fully qualified charm URI. - -**Example:** - -```text -charm: containers-easyrsa -``` - - - -## `applications..constraints` - -```text - # - # constraints: - # - # Sets standard constraints for the application. As per normal behaviour, - # these become the application's default constraints (i.e. units added - # subsequent to bundle deployment will have these constraints applied). - # - - constraints: root-disk=8G - - constraints: cores=4 mem=4G root-disk=16G - - constraints: zones=us-east-1a - - constraints: "arch=amd64 mem=4G cores=4" - -``` - -## `applications..devices` - -## `applications..expose` - -```text - # - # expose: - # - # Exposes the application using a boolean value. The default value is - # 'false'. - # - # In order to use the granular per-endpoint expose settings feature - # (Juju 2.9 or newer) by specifying an "exposed-endpoints" section, the - # expose field must either be set to false or omitted from the bundle. - # - - expose: true - -``` - - - -## `applications..exposed-endpoints` - -```text - - # exposed-endpoints: - # - # Specifies the set of CIDRs and/or spaces that are allowed to access the - # port ranges opened by the application. Expose settings can be - # specified both for the entire application using the wildcard ("") key - # and for individual endpoints. - # - # NOTES: - # - This is a deployment-specific field and can only be specified - # as part of an overlay. - # - This field is supported since Juju 2.9. - -``` -Since Juju 2.9, operators can control the expose parameters (CIDRs and/or spaces that are allowed access to the port ranges opened by exposed applications) for the entire application and/or on a per-endpoint basis. - -Application expose parameters can also be specified in bundles. However, as expose parameters are deployment-specific, they can only be provided as part of an overlay. Consider the following multi-document bundle: - -```yaml -applications: - mysql: - charm: "mysql" - num_units: 1 ---- # overlay -applications: - mysql: - exposed-endpoints: - "": - expose-to-cidrs: - - 0.0.0.0/0 - - ::/0 - db-admin: - expose-to-spaces: - - dmz - expose-to-cidrs: - - 192.168.0.0/24 -``` - -This is equivalent to the following commands: - -```text -juju deploy mysql -juju expose mysql --to-cidrs 0.0.0.0/0,::/0 -juju expose mysql --endpoints db-admin --to-spaces dmz --to-cidrs 192.168.0.0/24 -``` - -As a result of the above commands, the mysql application will be exposed and: -- All port ranges opened by the charm for any endpoint **except** `db-admin` will be reachable by **any** IP address. -- Port ranges opened by the charm for the `db-admin` endpoint will only be reachable by IPs that are part of the `192.168.0.0/24` block or belong to a subnet associated with the `dmz` space. - -```{note} - -When using this particular feature, the bundle must not also contain an `expose: true` field or Juju will display an error when attempting to deploy the bundle. - -This constraint prevents operators from accidentally exposing **all** ports for an application when attempting to deploy such a bundle to a pre 2.9 controller as older controllers would honor the `expose: true` flag but would not interpret the `exposed-endpoints` field. - -In addition, 2.9 (and newer) Juju clients will also display an error when attempting to deploy a bundle containing an `exposed-endpoints` section to a pre 2.9 controller. - -``` - -## `applications..exposed-endpoints.expose-to-cidrs` - -## `applications..exposed-endpoints.expose-to-spaces` - - -## `applications..num_units` - -**Purpose: Specifies the number of units to deploy. - -**Value:** Integer = the number of units. Default: '0'. - -**Example:** - -```text -num_units: 2 -``` - -## `applications..offers.` -## `applications..offers..acl` -## `applications..offers..endpoints` -## `applications..offers` - -```text - - # offers: - # - # Specifies a list of offers for the application endpoints that can be - # consumed by other models. Each offer entry is identified by a unique - # name and must include a list of application endpoints to be exposed - # as part of the offer. In addition, each offer may optionally define an - # "acl" block to control, on a per-user level, the permissions granted to - # the consumer side. The "acl" block keys are user names and values are - # permission levels. - # - # NOTES: - # - This is a deployment-specific field and can only be specified - # as part of an overlay. - # - This field is supported since Juju 2.7. - # - - offers: - my-offer: - endpoints: - - apache-website - acl: - admin: admin - user1: read - -``` - -## `applications..options` - -```text - # - # options: - # - # Sets configuration options for the application. The keys are - # application-specific and are found within the corresponding charm's - # metadata.yaml file. An alias (a string prefixed by an asterisk) may be - # used to refer to a previously defined anchor (see the 'variables' - # element). - # - - options: - osd-devices: /dev/sdb - worker-multiplier: *worker-multiplier - -``` - -Values for options and annotations can also be read from a file. For binary files, such as binary certificates, there is an option to base64-encode the contents. A file location can be expressed with an absolute or relative (to the bundle file) path. For example: - -``` yaml -applications: - my-app: - charm: some-charm - options: - config: include-file://my-config.yaml - cert: include-base64://my-cert.crt -``` - -## `applications..placement` -## `applications..plan` - -```text - - # - # plan: - # - # This is for third-party Juju support only. It sets the "managed - # solutions" plan for the application. The string has the format - # '/'. - # - - plan: acme-support/default - -``` - -## `applications..resources` - -**Purpose:** States what charm resource to use. - -**Value:** Map. Keys are individual resources. - - -Bundles support charm resources (see {ref}`Using resources <5679md>`) through the use of the `resources` key. Consider the following charm `metadata.yaml` file that includes a resource called `pictures`: - -``` yaml -name: example-charm -summary: "example charm." -description: This is an example charm. -resources: - pictures: - type: file - filename: pictures.zip - description: "This charm needs pictures.zip to operate" -``` - -It might be desirable to use a specific resource revision in a bundle: - -``` yaml -applications: - example-charm: - charm: "example-charm" - series: trusty - resources: - pictures: 1 -``` - -So here we specify a revision of '1' from Charmhub. - -The `resources` key can also specify a local path to a resource instead: - -``` yaml -applications: - example-charm: - charm: "example-charm" - series: trusty - resources: - pictures: "./pictures.zip" -``` - -Local resources can be useful in network restricted environments where the controller is unable to contact Charmhub. - -## `applications..resources.` - -**Purpose:** Defines individual resources. - -**Name:** Application specific. Cf. the charm's `metadata.yaml`. - -**Value:** Integer = the resource revision stored in the Charmhub or String = absolute or relative file path to local resource. - -**Examples:** - -```text -easyrsa: 5 -``` -```text -easyrsa: ./relative/path/to/file -``` - - -## `applications..revision` - -**Purpose:** States the revision of the charm should be used when deploying a non-local charm. Use requires a channel to be specified, indicating which channel should be used when refreshing the charm. - -**Example:** - -```text -revision: 8 -``` - -

`applications..scale` -

- # The application's existing units are iterated over in ascending - # order, with each one being assigned as the destination for a unit to - # be placed. New machines are used when 'num_units' is greater than the - # number of available units. The same results can be obtained by - # stating the units explicitly with the 'unit' type above. - # - - to: ["django"] - - # - # :new - # Unit is placed inside a container on a new machine. The value for - # `` can be either 'lxd' or 'kvm'. A new machine is the - # default and does not require stating, so ["lxd:new"] or just ["lxd"]. - # - - to: ["lxd"] - - # - # : - # Unit is placed inside a new container on an existing machine. - # - - to: ["lxd:2", "lxd:3"] - - # - # : - # Unit is placed inside a container on the machine that hosts the - # specified unit. If the specified unit itself resides within a - # container, then the resulting container becomes a peer (sibling) of - # the other (i.e. no nested containers). - # - - to: ["lxd:nova-compute/2", "lxd:glance/3"] -``` - -

`applications..trust`

- -

`bundle`

- -If set to `kubernetes`, indicates a Kubernetes bundle. - -

`default-base`

- -

`description`

- -**Purpose:** Sets the bundle description visible on Charmhub. - -**Examples:** - -```text -description: This is a test bundle. -``` - -```text -description: | - - This description is long and has multiple lines. Use the vertical bar as - shown in this example. -``` - - -## `docs` - - -```text -# (Optional) A link to a documentation cover page on Discourse -# More details at https://juju.is/docs/sdk/charm-documentation -docs: - -``` - -## `issues` - -**Example:** - -```text -# (Optional) A string (or a list of strings) containing a link (or links) to the bundle bug tracker. -issues: | [] -``` - - -## `machines` - -```text - -# machines: -# -# Provides machines that have been targeted by the 'to' key under the -# '' element. A machine is denoted by that same machine ID, -# and must be quoted. Keys for 'constraints', 'annotations', and 'series' can -# optionally be added to each machine. Containers are not valid machines in -# this context. -# - -machines: - "1": - "2": - series: bionic - constraints: cores=2 mem=2G - "3": - constraints: cores=3 root-disk=1T - - -``` - -## `machines..annotations` - - - -## `machines..base` -## `machines..constraints` -## `machines..series` -## `name` - -**Example:** - -```text -# name: -# -# Name defines an optional name for the bundle. Used only for Charmhub -# Store and is omitted for other stores (charmstore, private) and local -# deployments. -# - -name: foo -``` - - -## `relations` - - -**Example:** - -```text -# -# relations: -# -# States the relations to add between applications. Each relation consists of a -# pair of lines, where one line begins with two dashes and the other begins -# with a single dash. Each side of a relation (each line) has the format -# ':', where 'application' must also be represented -# under the 'applications' element. Including the endpoint is not strictly -# necessary as it might be determined automatically. However, it is best -# practice to do so. -# - -relations: -- - kubernetes-master:kube-api-endpoint - - kubeapi-load-balancer:apiserver -- - kubernetes-master:loadbalancer - - kubeapi-load-balancer:loadbalancer - -``` - -## `saas` - -**Example:** - -```text -# -# saas: -# -# Specifies a set of offers (from the local or a remote controller) to consume -# when the bundle is deployed. Each entry in the list is identified via a unique -# name and a URL to the offered service. Offer URLs have the following format: -# [:][/]. -# -# If the controller name is omitted, Juju will use the currently active controller. -# Similarly, if the model owner is omitted, Juju will use the user that is currently -# logged in to the controller providing the offer. -# - -saas: - svc1: - url: localoffer.svc1 - svc2: - url: admin/localoffer.svc2 - svc3: - url: othercontroller:admin/offer.svc3 -``` - - -## `saas.` -## `saas..url` -## `series` - -```text -# series: -# -# Sets the default series for all applications in the bundle. This also affects -# machines devoid of applications. See 'Charm series' above for how a final -# series is determined. -``` - -**Example:** - -```text -series: bionic -``` - -What series a charm will use can be influenced in several ways. Some of these are set within the bundle file while some are not. When using bundles, the series is determined using rules of precedence (most preferred to least): - -- the series stated for a machine that an application unit has been assigned to (see `series` under the `` element) -- the series stated for an application (see `series` under the `` element) -- the series given by the top level `series` element -- the top-most series specified in a charm's `metadata.yaml` file -- the most recent LTS release - - - -## `source` - -**Example:** - -```text - -# (Optional) A string (or a list of strings) containing a link (or links) to the bundle source code. -source: | [] -``` - -## `tags` - -```text -# tags: -# -# Sets descriptive tags. A tag is used for organisational purposes in the -# Charm Store. See https://docs.jujucharms.com/authors-charm-metadata -# for the list of valid tags. -``` - -**Examples:** - -```text -tags: [monitoring] -``` - -```text -tags: [database, utility] -``` - - -## `type` - -## `variables` - - -```text -# variables: -# -# Includes the optional definition of variables using anchors. Corresponding -# values are later manifested with the use of aliases. An anchor is a string -# prefixed with an ampersand (&) whereas an alias is the same string prefixed -# by an asterisk (*). The alias will typically be used to specify a value for an -# application option (see element 'options'). -``` - -**Example:** - -```text -variables: - data-port: &data-port br-ex:eno2 - worker-multiplier: &worker-multiplier 0.25 - -``` - -## `website` - - -```text -# (Optional) A string (or a list of strings) containing a link (or links) to project websites. -# In general this is likely to be the upstream project website, or the formal website for the -# charmed bundle. -website: | [] - -``` diff --git a/docs/reference/files/file-bundle-yaml.rst b/docs/reference/files/file-bundle-yaml.rst new file mode 100644 index 000000000..4fd2ae9e0 --- /dev/null +++ b/docs/reference/files/file-bundle-yaml.rst @@ -0,0 +1,856 @@ +.. _file-bundle-yaml: +.. highlight:: yaml + +``bundle.yaml`` +*************** + + See also: `Bundle `_ + + Source for the keys used by Juju: + `Schema `_, + `Examples from test files `_ + + (The metadata keys ``docs``, ``issues``, ``source``, and ``website`` are only used by Charmhub. + +File ``.yaml`` is the file in your bundle directory where you define your bundle. + +.. important:: + + ``bundle.yaml`` is typically generated using `Juju's export-bundle command `_. + +**For overlay bundles:** + +- Instead of providing overlays as external files, you may alternatively leverage Juju's support for multi-document YAML files and provide both the base overlay and any required overlays as a *single file*, appending the contents of the overlay after the base bundle using the special YAML document separator token `---` as the delimiter. Juju will treat the first document as the base bundle and any subsequent document as an overlay. + + + .. dropdown:: Example base and overlay in the same file + + .. code:: + + applications: + mysql: + charm: "mysql" + num_units: 1 + to: ["lxd:wordpress/0"] + --- # This is part of overlay 1 + applications: + mysql: + num_units: 1 + --- # This is part of overlay 2 + applications: + mysql: + trust: true + +- Relative paths are resolved relative to the path of the entity that describes them. That is, relative to the overlay bundle file itself. +- An application is removed from the base bundle by defining the application name in the application section, but omitting any values. Removing an application also removes all the relations for that application. +- If a machines section is specified in an overlay bundle, it replaces the corresponding section of the base bundle. No merging of machine information is attempted. Multiple overlay bundles can be specified and they are processed in the order they appear on the command line. +- Overlays can include new integrations, which are normally required for any new charms which have been added. Existing integrations cannot be removed however, except in the case where the referenced application is also removed by the overlay. + + +.. dropdown:: Example ``bundle.yaml`` file -- Kubernetes + + .. code:: + + bundle: kubernetes + applications: + postgresql: + charm: postgresql-k8s + scale: 3 + constraints: mem=1G + storage: + database: postgresql-pv,20M + mattermost: + charm: mattermost-k8s + placement: foo=bar + scale: 1 + relations: + - - postgresql:db + - mattermost:db + +.. dropdown:: Example ``bundle.yaml`` file -- machines + + A bundle for deployment on machines, for example, the `kubernetes-core `_ bundle, looks as follows: + + .. code:: + + description: A highly-available, production-grade Kubernetes cluster. + issues: https://bugs.launchpad.net/charmed-kubernetes-bundles + series: jammy + source: https://github.com/charmed-kubernetes/bundle + website: https://ubuntu.com/kubernetes/charmed-k8s + name: charmed-kubernetes + applications: + calico: + annotations: + gui-x: '475' + gui-y: '605' + channel: 1.26/stable + charm: calico + options: + vxlan: Always + containerd: + annotations: + gui-x: '475' + gui-y: '800' + channel: 1.26/stable + charm: containerd + easyrsa: + annotations: + gui-x: '90' + gui-y: '420' + channel: 1.26/stable + charm: easyrsa + constraints: cores=1 mem=4G root-disk=16G + num_units: 1 + etcd: + annotations: + gui-x: '800' + gui-y: '420' + channel: 1.26/stable + charm: etcd + constraints: cores=2 mem=8G root-disk=16G + num_units: 3 + options: + channel: 3.4/stable + kubeapi-load-balancer: + annotations: + gui-x: '450' + gui-y: '250' + channel: 1.26/stable + charm: kubeapi-load-balancer + constraints: cores=1 mem=4G root-disk=16G + expose: true + num_units: 1 + kubernetes-control-plane: + annotations: + gui-x: '800' + gui-y: '850' + channel: 1.26/stable + charm: kubernetes-control-plane + constraints: cores=2 mem=8G root-disk=16G + num_units: 2 + options: + channel: 1.26/stable + kubernetes-worker: + annotations: + gui-x: '90' + gui-y: '850' + channel: 1.26/stable + charm: kubernetes-worker + constraints: cores=2 mem=8G root-disk=16G + expose: true + num_units: 3 + options: + channel: 1.26/stable + relations: + - - kubernetes-control-plane:loadbalancer-external + - kubeapi-load-balancer:lb-consumers + - - kubernetes-control-plane:loadbalancer-internal + - kubeapi-load-balancer:lb-consumers + - - kubernetes-control-plane:kube-control + - kubernetes-worker:kube-control + - - kubernetes-control-plane:certificates + - easyrsa:client + - - etcd:certificates + - easyrsa:client + - - kubernetes-control-plane:etcd + - etcd:db + - - kubernetes-worker:certificates + - easyrsa:client + - - kubeapi-load-balancer:certificates + - easyrsa:client + - - calico:etcd + - etcd:db + - - calico:cni + - kubernetes-control-plane:cni + - - calico:cni + - kubernetes-worker:cni + - - containerd:containerd + - kubernetes-worker:container-runtime + - - containerd:containerd + - kubernetes-control-plane:container-runtime + + +The rest of this document describes each key in this file. + +.. note:: + + A bundle for deployment on Kubernetes differs from a standard bundle in the following ways: + + - key ``bundle`` is given the value of ``kubernetes`` + - key ``num_units`` is replaced by key ``scale`` + - key ``to`` is replaced by key ``placement`` + + The value of ``placement`` is a key=value pair and is used as a Kubernetes node selector. + + +``applications`` +================ + +**Purpose:** Holds all the applications in your bundle. + +**Value:** Mapping. Keys are application names. + +``applications.`` +------------------------------ + +**Purpose:** Holds an application definition. + +**Name:** The name of the application. User-defined, usually identical to `applications..charm`_ + +``applications..annotations`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Affects the GUI only. It provides horizontal and vertical placement of the +application's icon on the GUI's canvas. Annotations are expressed in terms of ``x`` +and ``y`` coordinates. + +.. dropdown:: Example + + .. code:: + + annotations: + gui-x: 450 + gui-y: 550 + +``applications..base`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``applications..bindings`` + +Maps endpoints to network spaces. Used to constrain relations to specific subnets in +environments where machines have multiple network devices. The empty (``""``) key +represents all endpoints and can be used to specify the default space for any endpoint +that is not explicitly bound to a space. + +.. dropdown:: Example + + .. code:: + + bindings: + "": alpha + kube-api-endpoint: internal + loadbalancer: dmz + +``applications..channel`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Purpose:** States what the preferred channel should be used when deploying a non-local charm. + +.. note:: Charmhub charms expect ``//`` format (e.g., ``latest/stable``). + +.. dropdown:: Example + + .. code:: + + channel: latest/edge + +``applications..charm`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Purpose:** States what charm to use for the application. **If you're defining a public bundle:** Use a fully qualified charm URI. + +.. dropdown:: Example + + .. code:: + + charm: containers-easyrsa + +``applications..constraints`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets standard constraints for the application. As per normal behaviour, these become +the application's default constraints (i.e. units added subsequent to bundle +deployment will have these constraints applied). + +.. dropdown:: Examples + + .. code:: + + constraints: root-disk=8G + + .. code:: + + constraints: cores=4 mem=4G root-disk=16G + + .. code:: + + constraints: zones=us-east-1a + + .. code:: + + constraints: "arch=amd64 mem=4G cores=4" + +``applications..devices`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``applications..expose`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whether to expose the application to the outside network. Default is ``false``. + +In order to use the granular per-endpoint expose settings feature by specifying an +"exposed-endpoints" section, the expose field must either be set to ``false`` or +omitted from the bundle. + +.. dropdown:: Example + + .. code:: + + expose: true + +``applications..exposed-endpoints`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Specifies the set of CIDRs and/or spaces that are allowed to access the port ranges +opened by the application. Expose settings can be specified both for the entire +application using the wildcard (``""``) key and for individual endpoints. + +.. note:: + + This is a deployment-specific field and can only be specified as part of an overlay. + +Operators can control the expose parameters (CIDRs and/or spaces that are allowed +access to the port ranges opened by exposed applications) for the entire application +and/or on a per-endpoint basis. + +Application expose parameters can also be specified in bundles. However, as expose +parameters are deployment-specific, they can only be provided as part of an overlay. +Consider the following multi-document bundle: + +.. code:: + + applications: + mysql: + charm: "mysql" + num_units: 1 + --- # overlay + applications: + mysql: + exposed-endpoints: + "": + expose-to-cidrs: + - 0.0.0.0/0 + - ::/0 + db-admin: + expose-to-spaces: + - dmz + expose-to-cidrs: + - 192.168.0.0/24 + +This is equivalent to the following commands: + +.. code:: bash + + juju deploy mysql + juju expose mysql --to-cidrs 0.0.0.0/0,::/0 + juju expose mysql --endpoints db-admin --to-spaces dmz --to-cidrs 192.168.0.0/24 + +As a result of the above commands, the mysql application will be exposed and: + +- All port ranges opened by the charm for any endpoint **except** ``db-admin`` will be + reachable by **any** IP address. +- Port ranges opened by the charm for the ``db-admin`` endpoint will only be reachable + by IPs that are part of the ``192.168.0.0/24`` block or belong to a subnet associated + with the ``dmz`` space. + +.. note:: + + When using this particular feature, the bundle must not also contain an + ``expose: true`` field or Juju will display an error when attempting to deploy the + bundle. + + This constraint prevents operators from accidentally exposing **all** ports for an + application when attempting to deploy such a bundle to a pre 2.9 controller as older + controllers would honor the ``expose: true`` flag but would not interpret the + ``exposed-endpoints`` field. + + In addition, Juju 2.9 (and newer) clients will also display an error when + attempting to deploy a bundle containing an ``exposed-endpoints`` section to a + pre-2.9 controller. + + +``applications..num_units`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Purpose:** Specifies the number of units to deploy. + +**Value:** Integer = the number of units. + +**Default:** ``0`` + +.. dropdown:: Example + + .. code:: + + num_units: 2 + +``applications..offers`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Specifies a list of offers for the application endpoints that can be consumed by other +models. Each offer entry is identified by a unique name and must include a list of +application endpoints to be exposed as part of the offer. In addition, each offer may +optionally define an ``acl`` block to control, on a per-user level, the permissions +granted to the consumer side. The ``acl`` block keys are user names and values are +permission levels. + +.. note:: + + This is a deployment-specific field and can only be specified as part of an overlay. + +.. dropdown:: Example + + .. code:: + + offers: + my-offer: + endpoints: + - apache-website + acl: + admin: admin + user1: read + +``applications..options`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets configuration options for the application. The keys are application-specific and +are found within the corresponding charm's metadata.yaml file. An alias (a string +prefixed by an asterisk) may be used to refer to a previously defined anchor (see the +`variables`_ element). + +.. dropdown:: Example + + .. code:: + + options: + osd-devices: /dev/sdb + worker-multiplier: *worker-multiplier + +Values for options and annotations can also be read from a file. For binary files, +such as binary certificates, there is an option to base64-encode the contents. A file +location can be expressed with an absolute or relative (to the bundle file) path. + +.. dropdown:: Example + + .. code:: + + applications: + my-app: + charm: some-charm + options: + config: include-file://my-config.yaml + cert: include-base64://my-cert.crt + +``applications..placement`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``applications..plan`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is for third-party Juju support only. It sets the "managed solutions" plan for +the application. The string has the format ``/`` + +.. dropdown:: Example + + .. code:: + + plan: acme-support/default + +``applications..resources`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Purpose:** States what charm resource to use. + +**Value:** Map. Keys are individual resources. + +Bundles support charm resources through the use of the ``resources`` key. +Consider the following charm ``metadata.yaml`` file that includes a +resource called ``pictures``: + +.. code:: + + name: example-charm + summary: "example charm." + description: This is an example charm. + resources: + pictures: + type: file + filename: pictures.zip + description: "This charm needs pictures.zip to operate" + +It might be desirable to use a specific resource revision in a bundle: + +.. code:: + + applications: + example-charm: + charm: "example-charm" + series: trusty + resources: + pictures: 1 + +So here we specify a revision of ``1`` from Charmhub. + +The ``resources`` key can also specify a local path to a resource instead: + +.. code:: + + applications: + example-charm: + charm: "example-charm" + series: trusty + resources: + pictures: "./pictures.zip" + +Local resources can be useful in network restricted environments where the controller +is unable to contact Charmhub. + +``applications..resources.`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Purpose:** Defines individual resources. + +**Name:** Application specific. Cf. the charm's ``metadata.yaml``. + +**Value:** Integer (the resource revision stored in the Charmhub) or String (absolute +or relative file path to local resource). + +.. dropdown:: Examples + + .. code:: + + easyrsa: 5 + + .. code:: + + easyrsa: ./relative/path/to/file + +``applications..revision`` + +**Purpose:** States the revision of the charm should be used when deploying a non-local +charm. Use requires a channel to be specified, indicating which channel should be used +when refreshing the charm. + +.. dropdown:: Example + + .. code:: + + revision: 8 + +``applications..scale`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``applications..series`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``applications..storage`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sets storage constraints for the application. There are three such constraints: +``pool``, ``size`` and ``count``. The key (label) is application-specific and is +found within the corresponding charm's :ref:`file-metadata-yaml` file. A value string +is one that would be used in the argument to the ``--storage`` option for the +``deploy`` command. + +.. dropdown:: Example + + .. code:: + + storage: + database: ebs,10G,1 + +``applications..to`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dictates the placement (destination) of the deployed units in terms of machines, +applications, units, and containers that are defined elsewhere in the bundle. The +number of destinations cannot be greater than the number of requested units +(see `applications..num_units`_ above). Zones are not supported; +see `applications..constraints`_ instead. The value types are given +below. + +**Values:** + +``new``: Unit is placed on a new machine. This is the default value type. This type +also gets used if the number of destinations is less than than ``num_units``. + +````: Unit is placed on an existing machine denoted by its (unquoted) ID. + +.. dropdown:: Example: + + .. code:: + + to: 3, new + +````: Unit is placed on the same machine as the specified unit. Doing so must +not create a loop in the placement logic. The specified unit must be for an +application that is different from the one being placed. + +.. dropdown:: Example + + .. code:: + + to: ["django/0", "django/1", "django/2"] + +````: The application's existing units are iterated over in ascending +order, with each one being assigned as the destination for a unit to be placed. New +machines are used when ``num_units`` is greater than the number of available units. +The same results can be obtained by stating the units explicitly with the ``unit`` +type above. + +.. dropdown:: Example + + .. code:: + + to: ["django"] + +``:new``: Unit is placed inside a container on a new machine. The +value for ```` can be either ``lxd`` or ``kvm``. A new machine is the +default and does not require stating, so ``["lxd:new"]`` is equivalent to just +``["lxd"]``. + +.. dropdown:: Example + + .. code:: + + to: ["lxd"] + +``:``: Unit is placed inside a new container on an existing +machine. + +.. dropdown:: Example + + .. code:: + + to: ["lxd:2", "lxd:3"] + +``:``: Unit is placed inside a container on the machine that +hosts the specified unit. If the specified unit itself resides within a container, +then the resulting container becomes a peer (sibling) of the other (i.e. containers +are not nested). + +.. dropdown:: Example + + .. code:: + + to: ["lxd:nova-compute/2", "lxd:glance/3"] + +``applications..trust`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``bundle`` +========== + +If set to ``kubernetes``, indicates a Kubernetes bundle. + +``default-base`` +================ + +The default base for deploying charms that can be deployed on multiple bases. + +``description`` +=============== + +**Status:** Optional, but recommended. + +**Purpose:** Sets the bundle description visible on Charmhub. + +**Type:** String + +.. dropdown:: Examples + + .. code:: + + description: This is a test bundle. + + .. code:: + + description: | + This description is long and has multiple lines. Use the vertical bar as + shown in this example. + +``docs`` +======== + +**Status:** Optional, but recommended. + +**Purpose:** A link to a documentation cover page. + + See more: `Charm documentation `_ + + +``issues`` +========== + +**Status:** Optional + +**Purpose:** A string (or a list of strings) containing a link (or links) to the +bundle's bug tracker. + +.. dropdown:: Examples + + .. code:: + + issues: https://bugs.launchpad.net/my-bundle + + .. code:: + + issues: + - https://bugs.launchpad.net/my-bundle + - https://github.com/octocat/my-bundle/issues + +``machines`` +============ + +Provides machines that have been targeted by `applications..to`_. A +machine is denoted by that same machine ID, and must be quoted. Keys for +``constraints``, ``annotations`` and ``series`` can optionally be added to each +machine. Containers are not valid machines in this context. + +.. dropdown:: Example + + .. code:: + + machines: + "1": + "2": + series: bionic + constraints: cores=2 mem=2G + "3": + constraints: cores=3 root-disk=1T + +``name`` +======== + +**Status:** Optional. Only used by Charmhub. + +**Type:** String with the same limitations as a +:ref:`charm name `. + +``relations`` +============= + +States the relations to add between applications. Each relation consists of a pair +of lines, where one line begins with two dashes and the other begins with a single +dash. Each side of a relation (each line) has the format ``:``, +where ``application`` must also be represented under `applications`_. Including the +endpoint is not strictly necessary as it might be determined automatically. However, +it is best practice to do so. + +.. dropdown:: Example + + .. code:: + + relations: + - - kubernetes-master:kube-api-endpoint + - kubeapi-load-balancer:apiserver + - - kubernetes-master:loadbalancer + - kubeapi-load-balancer:loadbalancer + + +``saas`` +======== + +Specifies a set of offers (from the local or a remote controller) to consume when the +bundle is deployed. Each entry in the list is identified via a unique name and a URL +to the offered service. Offer URLs have the following format: + +.. code:: text + + [:][/]. + +If the controller name is omitted, Juju will use the currently active controller. +Similarly, if the model owner is omitted, Juju will use the user that is currently +logged in to the controller providing the offer. + +.. dropdown:: Example + + ..code:: + + saas: + svc1: + url: localoffer.svc1 + svc2: + url: admin/localoffer.svc2 + svc3: + url: othercontroller:admin/offer.svc3 + +``series`` +========== + +Sets the default series for all applications in the bundle. This also affects machines +devoid of applications. See 'Charm series' above for how a final series is determined. + +What series a charm will use can be influenced in several ways. Some of these are set +within the bundle file while some are not. When using bundles, the series is determined +using rules of precedence (most preferred to least): + +- the series stated for a machine that an application unit has been assigned to (see + `machines`_) +- the series stated for an application (see `series` under the `` element) +- the series given by the top level `series` element +- the top-most series specified in a charm's `metadata.yaml` file +- the most recent LTS release + +.. dropdown:: Example + + .. code:: + + series: noble + + +``source`` +========== + +**Status:** Optional + +**Purpose:** A string or list of strings containing a link (or links) to the bundle source code. + +``tags`` +======== + +Sets descriptive tags. A tag is used for organisational purposes in the Charm Store. + +.. dropdown:: Examples + + .. code:: + + tags: [monitoring] + + .. code:: + + tags: [database, utility] + +``type`` +======== + +``variables`` +============= + +Includes the optional definition of variables using anchors. Corresponding values are +later manifested with the use of aliases. An anchor is a string prefixed with an +ampersand (&) whereas an alias is the same string prefixed by an asterisk (*). +The alias will typically be used to specify a value for an application option +(see element ``options``). + +.. dropdown:: Example + + .. code:: + + variables: + data-port: &data-port br-ex:eno2 + worker-multiplier: &worker-multiplier 0.25 + + +``website`` +=========== + +**Status:** Optional + +**Structure:** A string (or a list of strings) containing a link (or links) to +project websites. In general this is likely to be the upstream project website or the +formal website for the charmed bundle. + +.. _juju-bundle: https://juju.is/docs/juju/bundle +.. _juju-export-bundle: https://juju.is/docs/juju/juju-export-bundle From 2c31ea55b7b3393de19279759aba6bb810079af2 Mon Sep 17 00:00:00 2001 From: Teodora Mihoc Date: Thu, 19 Dec 2024 16:48:46 +0100 Subject: [PATCH 10/19] docs: remove md files already converted to rst --- docs/howto/index.md | 23 - docs/howto/manage-bundles.md | 40 -- docs/howto/manage-channels.md | 75 --- docs/howto/manage-charms.md | 507 ------------------ docs/howto/manage-extensions.md | 209 -------- docs/howto/manage-icons.md | 70 --- docs/howto/manage-libraries.md | 111 ---- docs/howto/manage-names.md | 36 -- docs/howto/manage-parts.md | 48 -- docs/howto/manage-resources.md | 97 ---- docs/howto/manage-revisions.md | 81 --- docs/howto/manage-the-charmcraft-cli.md | 119 ---- .../howto/manage-the-current-charmhub-user.md | 99 ---- docs/howto/manage-tracks.md | 101 ---- docs/howto/misc/index.md | 16 - ...ack-a-hooks-based-charm-with-charmcraft.md | 158 ------ ...-a-reactive-based-charm-with-charmcraft.md | 126 ----- docs/index.md | 81 --- 18 files changed, 1997 deletions(-) delete mode 100644 docs/howto/index.md delete mode 100644 docs/howto/manage-bundles.md delete mode 100644 docs/howto/manage-channels.md delete mode 100644 docs/howto/manage-charms.md delete mode 100644 docs/howto/manage-extensions.md delete mode 100644 docs/howto/manage-icons.md delete mode 100644 docs/howto/manage-libraries.md delete mode 100644 docs/howto/manage-names.md delete mode 100644 docs/howto/manage-parts.md delete mode 100644 docs/howto/manage-resources.md delete mode 100644 docs/howto/manage-revisions.md delete mode 100644 docs/howto/manage-the-charmcraft-cli.md delete mode 100644 docs/howto/manage-the-current-charmhub-user.md delete mode 100644 docs/howto/manage-tracks.md delete mode 100644 docs/howto/misc/index.md delete mode 100644 docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md delete mode 100644 docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md delete mode 100644 docs/index.md diff --git a/docs/howto/index.md b/docs/howto/index.md deleted file mode 100644 index f4e977f3c..000000000 --- a/docs/howto/index.md +++ /dev/null @@ -1,23 +0,0 @@ -(how-to-guides)= -# How-to guides - - -```{toctree} -:maxdepth: 2 - -Manage the `charmcraft` CLI -Manage charms -Manage charms (12-factor apps) -Manage extensions -Manage resources -Manage libraries -Manage parts -Manage the current Charmhub user -Manage names -Manage revisions -Manage channels -Manage tracks -Manage icons -Misc -Manage bundles -``` diff --git a/docs/howto/manage-bundles.md b/docs/howto/manage-bundles.md deleted file mode 100644 index cacceafcd..000000000 --- a/docs/howto/manage-bundles.md +++ /dev/null @@ -1,40 +0,0 @@ -(how-to-manage-charm-bundles)= -# How to manage charm bundles - -> See first: [`juju` | Bundle](https://juju.is/docs/juju/bundle) - -```{important} -Starting with 1 Jan 2025, bundles are being phased out. - -``` - -## Create a bundle - -To create a bundle, create a `.yaml` file with your desired configuration. - -```{tip} - -If you don't want to start from scratch, export the contents of your model to a `.yaml` file via `juju export-bundle --filename .yaml` or download the `.yaml` of an existing bundle from Charmhub. - -> See more: [Juju | How to compare and export the contents of a model to a bundle](https://juju.is/docs/juju/manage-models#heading--compare-and-export-the-contents-of-a-model-to-a-bundle) - - -``` - -> See more: {ref}`file-bundle-yaml` - -## Pack a bundle - -To pack a bundle, in the directory where you have your `bundle.yaml` file (and possibly other files, e.g., a `README.md` file), create a `charmcraft.yaml` file suitable for a bundle (at the minimum: `type: bundle`), then run `charmcraft pack` to pack the bundle. The result is a `.zip` file. - -> See more: {ref}`ref_commands_pack` - - -## Publish a bundle on Charmhub - -The process is identical to that for a simple charm except that, at the step where you register the name, for bundles the command is `register-bundle`. - - -> See more: {ref}`publish-a-charm` - - diff --git a/docs/howto/manage-channels.md b/docs/howto/manage-channels.md deleted file mode 100644 index 10c132c27..000000000 --- a/docs/howto/manage-channels.md +++ /dev/null @@ -1,75 +0,0 @@ -(manage-channels)= -# How to manage channels - - -## Create a channel - -When you register a name on Charmhub, that automatically creates 4 channels, all with track `latest` but with different risk levels, namely, `edge`, `beta`, `candidate`, `stable`, respectively. - -> See more: {ref}`register-a-name` - - - -## View the available channels - -To view a charm's channels on Charmhub, run `charmcraft status` followed by the name of the charm. E.g., - -```bash -$ charmcraft status my-awesome-charm -Track Channel Version Revision -latest stable - - - candidate - - - beta 0.1 1 - edge ↑ ↑ -``` - -(The above output shows 4 channels, all of which have the same track, `latest`, but different risk levels, namely, `edge`, `beta`, `candidate`, and `stable`.) - -> See more: {ref}`command-charmcraft-status` - - -## Customise a channel's track - - -Request a track guardrail and create a track - -See {ref}`manage-tracks`. - - -## Open a channel - - -A channel is opened implicitly when you release a revision to it. - - - -## Close a channel - -A channel is opened when you release a revision to that channel. Before that, the channel is created but not opened. When you're closing a channel, e.g., latest/candidate, that means that any deployment requests that go there will be forwarded to the next most stable risk, e.g., for beta, latest/stable. If you close stable, you can no longer deploy or update from that, unless you release again to that channel (because releasing opens the channel). - -If you add a branch, closing that branch will forward people to the same track and risk, without a branch. diff --git a/docs/howto/manage-charms.md b/docs/howto/manage-charms.md deleted file mode 100644 index 09a7f8ccb..000000000 --- a/docs/howto/manage-charms.md +++ /dev/null @@ -1,507 +0,0 @@ -(manage-charms)= -# How to manage charms - -> See first: [`juju` | Charm](https://juju.is/docs/juju/charmed-operator), [`juju` | Manage charms](https://juju.is/docs/juju/manage-charms-or-bundles) - - -## Initialise a charm - -To initialise a charm project, create a directory for your charm, enter it, then run `charmcraft init` with the `--profile` flag followed by a suitable profile name (for machine charms: `machine`; for Kubernetes charms: `kubernetes`, `simple`, or `flask-framework`); that will create all the necessary files and even prepopulate them with useful content. - -```text -charmcraft init --profile -``` - - -````{dropdown} See sample session - - -```bash -$ mkdir my-flask-app-k8s -$ cd my-flask-app-k8s/ -$ charmcraft init --profile flask-framework -Charmed operator package file and directory tree initialised. - -Now edit the following package files to provide fundamental charm metadata -and other information: - -charmcraft.yaml -src/charm.py -README.md - -$ ls -R -.: -charmcraft.yaml requirements.txt src - -./src: -charm.py - - -``` - -```` - - - -The command also allows you to not specify any profile (in that case you get the `simple` profile -- a Kubernetes profile with lots of scaffolding, suitable for beginners) and has flags that you can use to specify a different directory to operate in, a charm name different from the name of the root directory, etc. - -> See more: {ref}`ref_commands_revisions`, {ref}`profile`, {ref}`list-of-files-in-a-charm-project` -> -> See more: {ref}`manage-extensions` - -## Add charm project metadata, an icon, docs - - -### Specify that the project is a charm - -To specify that the project is a charm (as supposed to a bundle), in your `charmcraft.yaml` file set the `type` key to `charm`: - -```text -type: charm -``` - -### Specify a name - -To specify a pack-and-deploy name for your charm, in your charm's `charmcraft.yaml` file specify the `name` key. E.g., - -```yaml -name: traefik-k8s -``` - -> See more: {ref}`file-charmcraft-yaml-name` - -### Specify a title - -To specify a title for your charm's page on Charmhub, in your charm's `charmcraft.yaml` file specify a value for the `title` key. E.g., - -```yaml -title: | - Traefik Ingress Operator for Kubernetes -``` - -> See more: {ref}`file-charmcraft-yaml-title` - -### Add a summary - -To add a summary line for your charm, in your charm's `charmcraft.yaml` file specify a value for the `summary` key. E.g., - -```yaml -summary: | - A Juju charm to run a Traefik-powered ingress controller on Kubernetes. -``` - -> See more: {ref}`file-charmcraft-yaml-summary` - -### Add a description - -To add a longer description for your charm, in your charm's `charmcraft.yaml` file specify a value for the `description` key. E.g., - -```yaml -description: | - A Juju-operated Traefik operator that routes requests from the outside of a - Kubernetes cluster to Juju units and applications. - -``` - -> See more: {ref}`file-charmcraft-yaml-description` - -### Add contact information - -To add maintainer contact information for a charm, in your charm's `charmcraft.yaml` file specify a value for the `links.contact` key. E.g., - -```yaml -links: - contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont -``` - -> See more: {ref}`file-charmcraft-yaml-contact` - -### Add a link to source code - -To add a link to the source code for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.source` key. E.g., - -```yaml -links: - source: - - https://github.com/canonical/traefik-k8s-operator -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add a link to the bug tracker - -To add a link to the bug tracker for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.issues` key. E.g., - -```yaml -links: - issues: - - https://github.com/canonical/traefik-k8s-operator/issues -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add a link to the website - -If your charm has a website outside of Charmhub, to add a link to this website, in your charm's `charmcraft.yaml` file specify an item under the `links.website` key. E.g., - -```yaml -links: - website: - - https://charmed-kubeflow.io/ -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add docs and a link to the docs - -If you publish your charm on Charmhub, reference documentation about the charm's resources, actions, configurations, relations, and libraries is extracted automatically. However, you should also aim to add further docs, e.g., a tutorial, how-to guides, etc. To provide a link to these docs, in your charm's `charmcraft.yaml` file specify a value for the `links.documentation` key. Note that at present this must be a Discourse page. E.g., - -```yaml -links: - documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 -``` - -> See more: {ref}`file-charmcraft-yaml-links` - -### Add terms of use - -To add terms of use for your charm, in your charm's `charmcraft.yaml` file specify a value for the `terms` key. E.g., - -```yaml -terms: -- Butterscotch is regal -- Cara is adorable -``` - -> See more: {ref}`file-charmcraft-yaml-terms` - - -### Add an icon - -See {ref}`manage-icons`. - - -## Add runtime details to a charm - -### Require a specific Juju version - -To require a specific Juju version for your charm, in your charm's `charmcraft.yaml` specify the `assumes` key. E.g., - -```yaml -assumes: - - juju >= 3.5 -``` - -> See more: {ref}`file-charmcraft-yaml-assumes` - -### Require a Kubernetes cloud - -To require a Kubernetes cloud for your charm, in your charm's `charmcraft.yaml` file specify the `assumes` key. E.g., - -```yaml -assumes: - - k8s-api -``` - -> See more: {ref}`file-charmcraft-yaml-assumes` - -### Require a specific base and platforms - -To require a specific base and platforms for your charm, in your charm's `charmcraft.yaml` file specify the `base`(,`build-base`,) and the `platforms keys. E.g., - -```{note} -In Charmcraft < 3.0 this was done via a single key: `bases`. - -``` - -```yaml -# The run time base, the base format is @, -# accepted bases are: -# - ubuntu@24.04 -base: -# The build time base, if not defined the base is also the build time -# base, in addition to valid bases, the build-base can be "devel" -# which would use the latest in development Ubuntu Series. -build-base: - -platforms: - # The supported platforms, may omit build-for if platform-name - # is a valid arch, valid architectures follow the Debian architecture names, - # accepted architectures are: - # - amd64 - # - arm64 - # - armhf - # - ppc64el - # - riscv64 - # - s390x - : - # The build time architecture - build-on: | - # The run time architecture - build-for: | -``` - -> See more: {ref}`file-charmcraft-yaml-base`, {ref}`build-base`, {ref}`file-charmcraft-yaml-platforms` - -### Specify container requirements - -To specify container requirements, in your charm's `charmcraft.yaml` file specify the `containers` key. - - -> See more: {ref}`file-charmcraft-yaml-containers` - - -### Specify associated resources - -See {ref}`manage-resources`. - -### Specify device requirements - -> See more: {ref}`file-charmcraft-yaml-devices` - -To specify container requirements, in your charm's `charmcraft.yaml` file specify the `devices` key. - -### Specify storage requirements - -To specify storage requirements, in your charm's `charmcraft.yaml` file specify the `storage` key. - -> See more: {ref}`file-charmcraft-yaml-storage` - -### Specify extra binding requirements - -To specify extra binding requirements, in your charm's `charmcraft.yaml` file specify the `extra-bindings` key. - -> See more: {ref}`file-charmcraft-yaml-extra-bindings` - -### Require subordinate deployment - -To require subordinate deployment for your charm (i.e., for it to be deployed to the same machine as another charm, called its 'principal'), in your charm's `charmcraft.yaml` file specify the `subordinate` key. - -> See more: {ref}`file-charmcraft-yaml-subordinate` - - -### Manage actions - -> See first: [`juju` | Action](https://juju.is/docs/juju/action), [`juju` | Manage actions](https://juju.is/docs/juju/manage-actions) - - -To declare an action in your charm, in your charm's `charmcraft.yaml` file specify the `actions` key. - -> See more: {ref}`file-charmcraft-yaml-actions` -> -> See next: [`ops` | Manage actions]() - - -### Manage configurations - -> See first: [`juju` | Application configuration](https://juju.is/docs/juju/configuration#heading--application-configuration), [`juju` | Manage applications > Configure](https://juju.is/docs/juju/manage-applications#configure-an-application) - -To declare a configuration option for your charm, in your charm's `charmcraft.yaml` specify the `config` key. - - -> See more: {ref}`file-charmcraft-yaml-config` -> -> See next: [`ops` | Manage configurations]() - - - -### Manage relations (integrations) - -> See first: [`juju` | Relation](https://juju.is/docs/juju/relation), [`juju` | Manage relations](https://juju.is/docs/juju/manage-relations) - -To declare a relation endpoint in your charm, in your charm's `charmcraft.yaml` specify the `peers`, `provides`, or `requires` key. - -> See more: {ref}`file-charmcraft-yaml-peers-provides-requires` -> -> See more: [`ops` | Manage relations (integrations)]() - - -### Specify necessary libs - -> See first: [`juju` | Library]() - - -See {ref}`manage-libraries`. - -### Manage secrets -> See first: [`juju` | User secret](https://juju.is/docs/juju/secret#heading--user) - -To make your charm capable of accepting a user secret, in your charm's `charmcraft.yaml` specify the `config` key with the `type` subkey set to `secret`. - -> See more: {ref}`file-charmcraft-yaml-config` -> -> See next: [`ops` | Manage secrets]() - -### Specify necessary parts - -See {ref}`manage-parts`. - -## Pack a charm - -To pack a charm directory, in the charm's root directory, run the command below: - -```text -charmcraft pack -``` - -This will fetch any dependencies (from PyPI, based on `requirements.txt`), compile any modules, check that all the key files are in place, and produce a compressed archive with the extension `.charm`. As you can verify, this archive is just a zip file with metadata and the operator code itself. - -````{dropdown} Expand to view a sample session for a charm called microsample-vm - - -```text -# Pack the charm: -~/microsample-vm$ charmcraft pack -Created 'microsample-vm_ubuntu-22.04-amd64.charm'. -Charms packed: - microsample-vm_ubuntu-22.04-amd64.charm - -# (Optional) Verify that this has created a .charm file in your charm's root directory: -~/microsample-vm$ ls -CONTRIBUTING.md charmcraft.yaml requirements.txt tox.ini -LICENSE microsample-vm_ubuntu-22.04-amd64.charm src -README.md pyproject.toml tests - -# (Optional) Verify that the .charm file is simply a zip file that contains -# everything you've packed plus any dependencies: -/microsample-vm$ unzip -l microsample-vm_ubuntu-22.04-amd64.charm | { head; tail;} -Archive: microsample-vm_ubuntu-22.04-amd64.charm - Length Date Time Name ---------- ---------- ----- ---- - 815 2023-12-05 12:12 README.md - 11337 2023-12-05 12:12 LICENSE - 250 2023-12-05 12:31 manifest.yaml - 102 2023-12-05 12:31 dispatch - 106 2023-12-01 14:59 config.yaml - 717 2023-12-05 12:31 metadata.yaml - 921 2023-12-05 12:26 src/charm.py - 817 2023-12-01 14:44 venv/setuptools/command/__pycache__/upload.cpython-310.pyc - 65175 2023-12-01 14:44 venv/setuptools/command/__pycache__/easy_install.cpython-310.pyc - 4540 2023-12-01 14:44 venv/setuptools/command/__pycache__/py36compat.cpython-310.pyc - 1593 2023-12-01 14:44 venv/setuptools/command/__pycache__/bdist_rpm.cpython-310.pyc - 6959 2023-12-01 14:44 venv/setuptools/command/__pycache__/sdist.cpython-310.pyc - 2511 2023-12-01 14:44 venv/setuptools/command/__pycache__/rotate.cpython-310.pyc - 2407 2023-12-01 14:44 venv/setuptools/extern/__init__.py - 2939 2023-12-01 14:44 venv/setuptools/extern/__pycache__/__init__.cpython-310.pyc ---------- ------- - 20274163 1538 files - - -``` - -```` - -The command has a number of flags that allow you to specify a different charm directory to pack, whether to force pack if there are linting errors, etc. - -> See more: {ref}`ref_commands_pack` - -```{caution} - -**If you've declared any resources :** This will *not* pack the resources. This means that, when you upload your charm to Charmhub (if you do), you will have to upload the resources separately. - -> See more: {ref}`manage-resources` - -``` - -```{important} - -When the charm is packed, a series of analyses and lintings will happen, you may receive warnings and even errors to help improve the quality of the charm. - -> See more: {ref}`Charmcraft analyzers and linters ` - -``` - -> See next: [`juju` | Deploy a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Debug a charm](), [`juju` | Update a local charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-local-charm) - -(publish-a-charm)= -## Publish a charm on Charmhub - -1. Log in to Charmhub: - -```text -charmcraft login -``` - -> See more: {ref}`manage-the-current-charmhub-user` - -2. Register your charm's name (the one you specified in `charmcraft.yaml` > `name`): - -```text -charmcraft register my-awesome-charm -``` - -> See more: {ref}`manage-names` - -```{note} -This automatically creates 4 channels, all with track latest but with different risk levels, namely, edge, beta, candidate, stable, respectively. See more: {ref}`manage-channels`. -``` - -3. Upload the charm to Charmhub: Use the `charmcraft upload` command followed by the your charm's filepath. E.g., if you are in the charm's root directory, - -```text -charmcraft upload my-awesome-charm.charm -Revision 1 of my-awesome-charm created -``` - -> See more: {ref}`ref_commands_upload` - -```{note} -Each time you upload a charm to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-charm-revisions`. -``` - - -4. If your charm has associated resources: These are not packed with the rest of the charm project, so you must upload them explicitly to Charmhub as well. For example: - -```text -$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin -Revision 1 created of resource 'someresource' for charm 'my-super-charm' -``` - -> See more: {ref}`manage-resources` - -```{note} -Each time you upload a resource to Charmhub, that creates a revision (unless you upload the exact same file again). See more: {ref}`manage-resource-revisions`. -``` - -5. Release the charm: To release a charm, release your revision of choice to the target release channel. E.g., - -```text -$ charmcraft release my-awesome-charm --revision=1 --channel=beta -Revision 1 of charm 'my-awesome-charm' released to beta -``` - -> See more: {ref}`manage-charm-revisions` - -```{note} - -This automatically opens the channel. See more: {ref}`manage-channels`. -``` - -> See next: [`juju` | Deploy a Charmub charm](https://juju.is/docs/juju/manage-charms-or-bundles#deploy-a-charm-bundle), [`juju` | Update a Charmhub charm](https://juju.is/docs/juju/manage-charms-or-bundles#update-a-charmhub-charm) - -```{tip} -To update the charm on Charmhub, repeat the upload and release steps. - -``` - - -```{important} - -Releasing a charm on Charmhub gives it a public URL. However, the charm will not appear in the Charmhub search results until it has passed formal review. To request formal review, reach out to the community to announce your charm and ask for a review by an experienced community member. - -> See more: [Discourse | review requests](https://discourse.charmhub.io/c/charmhub-requests/46) - - -Also, the point of publishing and having a charm publicly listed on Charmhub is so others can reuse it and potentially contribute to it as well. To publicize your charm: - -- [Write a Discourse post to announce your release.](https://discourse.charmhub.io/tags/c/announcements-and-community/33/none) - -- [Schedule a community workshop to demo your charm's capabilities.](https://discourse.charmhub.io/tag/community-workshop) - -- [Chat about it with your charmer friends.](https://matrix.to/#/#charmhub-charmdev:ubuntu.com) - - -``` - - - diff --git a/docs/howto/manage-extensions.md b/docs/howto/manage-extensions.md deleted file mode 100644 index ae16f8fa9..000000000 --- a/docs/howto/manage-extensions.md +++ /dev/null @@ -1,209 +0,0 @@ -(manage-extensions)= -# How to manage extensions - -> See also: {ref}`extension` - -## View all the available extensions - -To view all the available Rockcraft / Charmcraft extensions, run the `rockcraft list-extensions` / `charmcraft list-extensions` command. Sample session: - -```bash -$ charmcraft list-extensions -Extension name Supported bases Experimental bases ----------------- ----------------- -------------------- -flask-framework ubuntu@22.04 -``` - -> See more: [Rockcraft | `rockcraft list-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/list-extensions/), {ref}`ref_commands_list-extensions` - -## View details about the extension in use - -Suppose you've initialised a rock / charm with a profile that comes with an extension (currently, `flask-framework`), and your `rockcraft.yaml` / `charmcraft.yaml > extensions` lists this extension. - - -````{dropdown} See example context - -```bash -$ mkdir my-flask-app-k8s -$ cd my-flask-app-k8s/ -$ charmcraft init --profile flask-framework -Charmed operator package file and directory tree initialised. - -Now edit the following package files to provide fundamental charm metadata -and other information: - -charmcraft.yaml -src/charm.py -README.md - -user@ubuntu:~/my-flask-app-k8s$ ls -R -.: -charmcraft.yaml requirements.txt src - -./src: -charm.py - -$ cat charmcraft.yaml -# This file configures Charmcraft. -# See https://juju.is/docs/sdk/charmcraft-config for guidance. - -name: my-flask-app-k8s - -type: charm - -bases: - - build-on: - - name: ubuntu - channel: "22.04" - run-on: - - name: ubuntu - channel: "22.04" - -# (Required) -summary: A very short one-line summary of the flask application. - -# (Required) -description: | - A comprehensive overview of your Flask application. - -extensions: - - flask-framework - -# Uncomment the integrations used by your application -# requires: -# mysql: -# interface: mysql_client -# limit: 1 -# postgresql: -# interface: postgresql_client -# limit: 1 - - -``` - -```` - -To view details about what that extension is adding to your charm, set the `CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS` to `1`, then run `charmcraft expand-extensions`. For example: - - -```text -CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions -``` - - -````{dropdown} See effect given example context - -``` -$ CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions -*EXPERIMENTAL* extension 'flask-framework' enabled -name: my-flask-app-k8s -summary: A very short one-line summary of the flask application. -description: | - A comprehensive overview of your Flask application. -parts: - charm: - source: . - charm-entrypoint: src/charm.py - charm-binary-python-packages: [] - charm-python-packages: [] - charm-requirements: - - requirements.txt - charm-strict-dependencies: false - plugin: charm -type: charm -bases: -- build-on: - - name: ubuntu - channel: '22.04' - run-on: - - name: ubuntu - channel: '22.04' -actions: - rotate-secret-key: - description: Rotate the flask secret key. Users will be forced to log in again. - This might be useful if a security breach occurs. -assumes: -- k8s-api -containers: - flask-app: - resource: flask-app-image -peers: - secret-storage: - interface: secret-storage -provides: - metrics-endpoint: - interface: prometheus_scrape - grafana-dashboard: - interface: grafana_dashboard -requires: - logging: - interface: loki_push_api - ingress: - interface: ingress - limit: 1 -resources: - flask-app-image: - type: oci-image - description: flask application image. -config: - options: - webserver-keepalive: - type: int - description: Time in seconds for webserver to wait for requests on a Keep-Alive - connection. - webserver-threads: - type: int - description: Run each webserver worker with the specified number of threads. - webserver-timeout: - type: int - description: Time in seconds to kill and restart silent webserver workers. - webserver-workers: - type: int - description: The number of webserver worker processes for handling requests. - flask-application-root: - type: string - description: Path in which the application / web server is mounted. This configuration - will set the FLASK_APPLICATION_ROOT environment variable. Run `app.config.from_prefixed_env()` - in your Flask application in order to receive this configuration. - flask-debug: - type: boolean - description: Whether Flask debug mode is enabled. - flask-env: - type: string - description: What environment the Flask app is running in, by default it's 'production'. - flask-permanent-session-lifetime: - type: int - description: Time in seconds for the cookie to expire in the Flask application - permanent sessions. This configuration will set the FLASK_PERMANENT_SESSION_LIFETIME - environment variable. Run `app.config.from_prefixed_env()` in your Flask application - in order to receive this configuration. - flask-preferred-url-scheme: - type: string - default: HTTPS - description: Scheme for generating external URLs when not in a request context - in the Flask application. By default, it's "HTTPS". This configuration will - set the FLASK_PREFERRED_URL_SCHEME environment variable. Run `app.config.from_prefixed_env()` - in your Flask application in order to receive this configuration. - flask-secret-key: - type: string - description: The secret key used for securely signing the session cookie and - for any other security related needs by your Flask application. This configuration - will set the FLASK_SECRET_KEY environment variable. Run `app.config.from_prefixed_env()` - in your Flask application in order to receive this configuration. - flask-session-cookie-secure: - type: boolean - description: Set the secure attribute in the Flask application cookies. This - configuration will set the FLASK_SESSION_COOKIE_SECURE environment variable. - Run `app.config.from_prefixed_env()` in your Flask application in order to - receive this configuration. - -``` - -```` - - -> See more: [`rockcraft expand-extensions`](https://canonical-rockcraft.readthedocs-hosted.com/en/latest/reference/commands/expand-extensions/), {ref}`ref_commands_expand-extensions` - -
- -> **Contributors:** @lengau, @tmihoc diff --git a/docs/howto/manage-icons.md b/docs/howto/manage-icons.md deleted file mode 100644 index c03cfd0da..000000000 --- a/docs/howto/manage-icons.md +++ /dev/null @@ -1,70 +0,0 @@ -(manage-icons)= -# How to manage icons - - -## Learn about icon requirements and best practices - -See {ref}`file-icon-svg`. - - -## Create an icon - -Before you start you will need: - -- A vector graphic editor. We strongly recommend the cross-platform and most excellent [Inkscape](http://www.inkscape.org) for all your vector graphic needs. -- [The template file.](https://assets.ubuntu.com/v1/fc0260eb-icon.svg) (right-click > Save link as...) -- An existing logo you can import, or the ability to draw one in Inkscape. - -Once you have those, fire up Inkscape and we can begin! - -### 1. Open the template - - -From Inkscape load the `icon.svg` file. Select the Layer called "Background Circle", either from the drop down at the bottom, or from the layer dialog. - -![Open the template](manage-icons-create-1.png) - -### 3. Add colour - -In the menu, select **Object** and then **Fill and Stroke** to adjust the colour. - -![Add color](manage-icons-create-2.png) - - -### 2. Draw something - - -Draw your shape within the circle. If you already have a vector logo, you can import it and scale it within the guides. Inkscape also has plenty of drawing tools for creating complex images. - -If you import a bitmap image to use, be sure to convert it into a vector file and delete the bitmap. - -![Draw something](manage-icons-create-3.png) - -*Cloud icon: "Cloud by unlimicon from the Noun Project" [CC BY]* - - - -## Validate an icon - -You can validate your icon at [charmhub.io/icon-validator](https://charmhub.io/icon-validator). The page checks the most basic issues that prevent icons working. - -![Validate](manage-icons-validate.png) - - -```{note} - - -``` - -## Add an icon to its charm's Charmhub page - -To add the icon to the charm's Charmhub page, save it as `icon.svg`, place it in the root directory of the charm, and then publish the charm to a channel of the form `/stable` (e.g., `latest/stable`). - -```{note} - -That is because Charmhub only updates the metadata for a charm on stable channel releases [(by design)](https://snapcraft.io/blog/better-snap-metadata-handling-coming-your-way-soon). -So either release the revision with the icon to a `stable` channel and then roll it back, or wait until your charm is ready for a "stable" `stable` release. - -``` - -> See more: {ref}`publish-a-charm` diff --git a/docs/howto/manage-libraries.md b/docs/howto/manage-libraries.md deleted file mode 100644 index edfe1735c..000000000 --- a/docs/howto/manage-libraries.md +++ /dev/null @@ -1,111 +0,0 @@ -(manage-libraries)= -# How to manage libraries -> See first: [`juju` | Library]() - - -## Initialise a library - -> See also: {ref}``charmcraft create-lib` ` - -In your charm's root directory, run `charmcraft create-lib`: - -```text -# Initialise a charm library named 'demo' -$ charmcraft create-lib demo -``` - -```{note} - -Before creating a library, you must first register ownership of your charm’s name. See more: {ref}`publish-a-charm`. - -``` - -This will create a template file at `$CHARMDIR/lib/charms/demo/v0/demo.py`. - - -> See more: {ref}`ref_commands_create-lib`, {ref}`file-libname-py` - -Edit this file to write your library. - -```{important} - -A library must comprise a single Python file. If you write a library that feels too "big" for a single file, it is likely that the library should be split up, or that you are actually writing a full-on charm. - -``` - -> See next: [`ops` | Manage libraries]() - - -(publish-a-library)= -## Publish a library on Charmhub - - -```{caution} - -On Charmhub, a library is always associated with the charm that it was first created for. When you publisht it to Charmhub, it's published to the page of that charm. To be able to publish it, you need to be logged in to Charmhub as a user who owns the charm (see more: {ref}`publish-a-charm`) or as a user who is registered as a contributor to the charm (a status that can be requested via [Discourse](https://discourse.charmhub.io/). - -``` - -To publish a library on Charmhub, in the root directory of the charm that holds the library, run `charmcraft publish-lib` followed by the full library path on the template `charms..v.`. For example: - -```text -$ charmcraft publish-lib charms.demo.v0.demo -Library charms.demo.v0.demo sent to the store with version 0.1 -``` - -> See more: {ref}`ref_commands_publish-lib` - -This will upload the library's content to Charmhub. - -To update the library on Charmhub, update the `LIBAPI`/`LIBPATCH` metadata fields inside the library file, then repeat the publish procedure. - -```{caution} **About the metadata fields:** - -Most times it is enough to just increment `LIBPATCH` but, if you're introducing breaking changes, you must work with the major API version. - -Additionally, be mindful of the fact that users of your library will update it automatically to the latest PATCH version with the same API version. To avoid breaking other people's library usage, make sure to increment the `LIBAPI` version but reset `LIBPATCH` to `0`. Also, before adding the breaking changes and updating these values, make sure to copy the library to the new path; this way you can maintain different major API versions independently, being able to update, for example, your v0 after publishing v1. See more: {ref}`file-libname-py`. -``` - -> See more: {ref}`ref_commands_publish-lib` - - -To share your library with other charm developers, navigate to the host charm's Charmhub page, go to Libraries tab, then copy and share the URL at the top of the page. - - -## View the libs published for a charm - -The easiest way to find an existing library for a given charm is via `charmcraft list-lib`, as shown below. This will query Charmhub and show which libraries are published for the specified charm, along with API/patch versions. - - jdoe@machine:/home/jane/autoblog$ charmcraft list-lib blogsystem - Library name API Patch - superlib 1 0 - -The listing will not show older API versions; this ensures that new users always start with the latest version. - -Another good way to search for libraries is to explore the charm collection on [Charmhub](https://charmhub.io/). - -> See more: {ref}``charmcraft list-lib` ` - - -## Use a library - -In your charm's `charmcraft.yaml`, specify the `charm-libs` key with the desired libraries. - -> See more: {ref}`file-charmcraft-yaml-charm-libs` - - -In your charm's root directory, run `charmcraft fetch-libs`. Charmcraft will download the libraries to your charm's directory. - -> See more: {ref}`ref_commands_fetch-libs` - - -To use a library in your `src/charm.py`, import it using its fully-qualified path minus the `lib` part: - -```python -import charms.demo.v0.demo -``` - -To update your lib with the latest published version, repeat the process. - - - diff --git a/docs/howto/manage-names.md b/docs/howto/manage-names.md deleted file mode 100644 index 916e17dc3..000000000 --- a/docs/howto/manage-names.md +++ /dev/null @@ -1,36 +0,0 @@ -(manage-names)= -# How to manage names - - -(register-a-name)= -## Register a name on Charmhub - - -To register a name for your charm on Charmhub, use the `chamrcraft register` command followed by your desired name. E.g., - -```bash -$ charmcraft register my-awesome-charm -Congrats! You are now the publisher of 'my-awesome-charm' -``` - -> See more: {ref}`ref_commands_register` - -This also automatically creates four channels, all with track `latest` but risk level `edge`, `beta`, `candidate`, and `stable`, respectively. - -> See more: {ref}`manage-channels` - -## View registered names - -To view the names you've registered on Charmhub, run `charmcraft names`. - -> See more: {ref}`ref_commands_names` - -## Unregister a name - -```{caution} -A name can be unregistered only if you haven't yet uploaded anything to it. -``` - -To unregister a name, run `charmcraft unregister` followed by the name. - -> See more: {ref}`ref_commands_unregister` diff --git a/docs/howto/manage-parts.md b/docs/howto/manage-parts.md deleted file mode 100644 index f9da9c76b..000000000 --- a/docs/howto/manage-parts.md +++ /dev/null @@ -1,48 +0,0 @@ -(manage-parts)= -# How to manage parts - -> See first: {ref}`part` - - -## Remove a part's assets - -TBA - -> See more: {ref}`ref_commands_clean` - - -## Download or retrieve artifacts defined for a part - -TBA - -> See more: {ref}`ref_commands_pull` - - -## Build artifacts defined for a part - -TBA - -> See more: {ref}`ref_commands_build` - - -## Stage built artifacts into a common staging area - -TBA - -> See more: {ref}`ref_commands_build` - - -## Prime artifacts defined for a part - -TBA - -> See more: {ref}`ref_commands_prime` - - -## Build the charm or bundle - - -TBA - -> See more: {ref}`ref_commands_pack` - diff --git a/docs/howto/manage-resources.md b/docs/howto/manage-resources.md deleted file mode 100644 index b6edd18b8..000000000 --- a/docs/howto/manage-resources.md +++ /dev/null @@ -1,97 +0,0 @@ -manage-resources)= -# How to manage resources - -> See first: [`juju` | Charm resource](https://juju.is/docs/juju/charm-resource), [`juju` | Manage resources](https://juju.is/docs/juju/manage-charm-resources) - -## Declare a resource - -To declare a resource required by your charm, in your charm's `charmcraft.yaml file` specify the `resources` key. - -> See more: {ref}`file-charmcraft-yaml-resources` -> -> See next: [`ops` | Manage resources]() - - -````{tip} -During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. For example, assuming the resource is a `/tmp/somefile.txt` file, you could pack and the deploy with `juju deploy ... --resource`: - -```text -echo "TEST" > /tmp/somefile.txt -charmcraft pack -juju deploy ./my-charm.charm --resource my-resource=/tmp/somefile.txt -``` - -```` - -(publish-a-resource)= -## Publish a resource on Charmhub - -```{note} -You must have already published the charm. See more: {ref}`publish-a-charm`. - -``` - -To publish a resource on its charm's Charmhub page, run the `charmcraft upload-resource` command followed by the name of the charm, the name of the resource (cf. `charmcraft.yaml`), and `--filepath=` / `--image=`. For example: - -```{note} - -The option `--image` must indicate an OCI image's digest, being it in the short or long form (e.g.: `70aa8983ec5c` or `sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249`). When using the "short form" of the digest, the image needs to be present locally so its proper ID (the "long form") can be retrieved. - - -``` - - -```text -$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin -Revision 1 created of resource 'someresource' for charm 'my-super-charm' -``` - -```text -$ charmcraft upload-resource my-super-charm redis-image --image=sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249 -Revision 1 created of resource 'redis-image' for charm 'my-super-charm' -``` - -Charmcraft will first check if that specific image is available in Canonical's Registry, and just use it if that's the case. If not, it will try to get it from the developer's local OCI repository (needs `dockerd` to be installed and running), push it to the Canonical's Registry, and then use it. Either way, when the upload has completed, you end up with a resource revision. - -To update a pre-uploaded resource, run the `upload-resource` command again. The result will be a new revision. - -> See more: {ref}`ref_commands_upload-resource` - -## View all the resources published on Charmhub - -To view all the resources published on Charmhub for a charm, run `charmcraft resources` followed by the charm name: - -```{important} - -**If you're not logged in to Charmhub:** The command will open up a browser window and ask you to log in. - -``` - -```text -$ charmcraft resources mycharm -``` - -> See more: {ref}`ref_commands_resources` - -(manage-resource-revisions)= -## Manage resource revisions -### List all the available resource revisions - -To view all the revisions for a resource associated with a charm you've uploaded to Charmhub, run `charmcraft resource-revisions` followed by the charm name and the resource name. For example: - -```text -$ charmcraft resource-revisions mycharm myresource -``` - -> See more: {ref}`ref_commands_resource-revisions` - -### Set the architectures for a resource revision - -To set the architectures for a revision of a resource associated with a charm you've uploaded to Charmhub, run `charmcraft set-resource-architectures` followed by the name of the charm, the name of the resource, and the architecture(s), using the `--resources` flag to specify the target resource revision. For example: - -```text -$ charmcraft set-resource-architectures mycharm myresource --revision=1 arm64,armhf -``` - -> See more: {ref}`ref_commands_set-resource-architectures` - diff --git a/docs/howto/manage-revisions.md b/docs/howto/manage-revisions.md deleted file mode 100644 index 83bcecc9a..000000000 --- a/docs/howto/manage-revisions.md +++ /dev/null @@ -1,81 +0,0 @@ -(manage-charm-revisions)= -# How to manage charm revisions - - - -## Create a charm revision - -A charm revision is created implicitly every time you upload a charm to Charmhub (unless you're uploading the exact same file again). - -> See more: {ref}`publish-a-charm` - -## View the existing charm revisions - - - To inspect the existing charm revisions, run `charmcraft revisions` followed by the name of the charm. - -> See more: {ref}`ref_commands_revisions` - - -## Promote a charm revision to a better risk level - -To promote a charm revision to a higher-ranking risk level, - - - -use the GitHub `promote-charm` action. - -> See more: [GitHub | canonical/charming-actions/promote-charm](https://github.com/canonical/charming-actions/tree/2.6.0/promote-charm) - -````{dropdown} Example outcome - -For example, in the (partial) output of juju info mongodb below, revision 100 has been promoted from `3.6/edge` through `3.6/beta` and `3.6/candidate` all the way to `3.6/stable`. (The up arrow next to `3.6/beta` indicates that that channel has been closed and, if you try `juju deploy --channel 3.6/beta`, what you’ll get is the next higher-ranking risk level of the same track, that is, `3.6/candidate`.) - -```text -channels: | - 5/stable: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 - 5/candidate: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 - 5/beta: ↑ - 5/edge: 118 2023-05-03 (118) 13MB amd64 ubuntu@22.04 - 3.6/stable: 100 2023-04-28 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 - 3.6/candidate: 100 2023-04-13 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 - 3.6/beta: ↑ - 3.6/edge: 100 2023-02-03 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 - -``` -```` - - -(release-a-revision-into-a-channel)= -## Release a charm revision into a channel - -To release a specific charm revision to a channel, run `charmcraft release` followed by the name of the charm and flags specifying the revision and its target channel. E.g., - -```text -$ charmcraft release my-awesome-charm --revision=1 --channel=beta -Revision 1 of charm 'my-awesome-charm' released to beta -``` - -> See more: {ref}`ref_commands_release` - - -This opens the channel you're releasing to. - - -> See more: {ref}`manage-channels` - - -Following the release, Charmhub will display the charm's information at `charmhub.io/`. (The default information displayed is obtained from the most stable channel.) Your charm will also become available for download. - -> See more: [`juju` | Download a charm from Charmhub](https://juju.is/docs/juju/manage-charms-or-bundles#download-a-charmhub-charm) - - diff --git a/docs/howto/manage-the-charmcraft-cli.md b/docs/howto/manage-the-charmcraft-cli.md deleted file mode 100644 index b4037f811..000000000 --- a/docs/howto/manage-the-charmcraft-cli.md +++ /dev/null @@ -1,119 +0,0 @@ -(manage-the-charmcraft-cli)= -# How to manage the `charmcraft` CLI - -> See first: {ref}`charmcraft-cli` - -## Install the `charmcraft` CLI - - -### On Linux - -The recommended way to install Charmcraft on Linux is from the `stable` channel via snap: - - sudo snap install charmcraft --classic - -There are multiple channels other than `stable`. See the full list with `snap info charmcraft`. - -We recommend either `latest/stable` or `latest/candidate` for everyday charming. With the snap you will always be up to date as Charmhub services and APIs evolve. Charmcraft supports Kubernetes operator development. - -In Linux, Charmcraft defaults to LXD to build the charms in a container matching the target base(s) (Multipass can also be used). Charmcraft will offer to install LXD if required, but here are steps to set it up manually: - -```text -$ sudo snap install lxd -$ sudo adduser $USER lxd -$ newgrp lxd -$ lxd init --auto -``` - -You can also install Charmcraft in an isolated environment. - -> See more: {ref}`install-in-an-isolated-environment` - -### On macOS - -Charmcraft is [available on homebrew](https://formulae.brew.sh/formula/charmcraft). - -Installation should be straightforward if using homebrew (if not already setup, refer to [this instructions](https://brew.sh/)). - -```text -$ brew install charmcraft -==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/manifests/1.3.2 -######################################################################## 100.0% -==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d96bbcfb72bdc -==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d -######################################################################## 100.0% -==> Pouring charmcraft--1.3.2.mojave.bottle.tar.gz -🍺 /usr/local/Cellar/charmcraft/1.3.2: 2,205 files, 17.2MB -``` - -Charmhub commands work natively: - -```text -$ charmcraft whoami -name: John Doe -username: jdoe -id: xxxxxxxxxxxxxxxxxxxxxxxxx -``` - -In macOS, Charmcraft defaults to Multipass to build the charms in a container matching the target base(s). Running pack asks to setup Multipass if not already installed, and continues with the packing process: - -```text -$ charmcraft pack -Multipass is required, but not installed. Do you wish to install Multipass and configure it with the defaults? [y/N]: y -==> Downloading https://github.com/canonical/multipass/releases/download/v1.7.2/multipass-1.7.2+mac-Darwin.pkg -Already downloaded: /Users/jdoe/Library/Caches/Homebrew/downloads/4237fcef800faa84459a2911c3818dfa76f1532d693b151438f1c8266318715b--multipass-1.7.2+mac-Darwin.pkg -==> Installing Cask multipass -==> Running installer for multipass; your password may be necessary. -Package installers may write to any location; options such as `--appdir` are ignored. -installer: Package name is multipass -installer: Installing at base path / -installer: The install was successful. -🍺 multipass was successfully installed! -Packing charm 'test-charm_ubuntu-20.04-amd64.charm'... -Starting charmcraft-test-charm-12886917363-0-0-amd64 ... -``` - -You can also install Charmcraft in an isolated environment. - -> See more: {ref}`install-in-an-isolated-environment` - - -### On Windows - -There is no previously packaged way to install Charmcraft in Windows, please refer to how to install it in an [isolated environment](#heading--isolated). - - -(install-in-an-isolated-environment)= -### In an isolated environment on Linux, macOS, or Windows - -One way to install Charmcraft is via [Multipass](https://multipass.run/). This is a good way to install it on any platform, as it will give you an isolated development environment. - -First, [install Multipass](https://multipass.run/docs/how-to-install-multipass). - -Second, use Multipass to provision a virtual machine. The following command will launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name 'charm-dev': - -```text -$ multipass launch --cpus 4 --memory 8G --disk 20G --name charm-dev -``` - -Last, open a shell in your new Ubuntu virtual machine, and install Charmcraft there: - -```text -$ multipass shell charm-dev -... -ubuntu@charm-dev:~$ sudo snap install charmcraft --classic -charmcraft 2.2.0 from Canonical✓ installed -``` - -That's it. You can now start typing in Charmcraft commands. - - -## Check the installed version of the `charmcraft` CLI - -To check the installed version, run: - -```text -charmcraft version -``` - -> See more: {ref}`ref_commands_version` diff --git a/docs/howto/manage-the-current-charmhub-user.md b/docs/howto/manage-the-current-charmhub-user.md deleted file mode 100644 index bb3f9c597..000000000 --- a/docs/howto/manage-the-current-charmhub-user.md +++ /dev/null @@ -1,99 +0,0 @@ -(manage-the-current-charmhub-user)= -# How to manage the current Charmhub user -> See first: [`juju` | Charmhub](), [Charmhub](https://charmhub.io/) - -## Log in to Charmhub - -### Local environments - -To log in to Charmhub, run `charmcraft login`: - -``` -$ charmcraft login -Opening an authorization web page in your browser. -If it does not open, please open this URL: -... -``` - -> See more: {ref}`ref_commands_login` - - -### Remote environments - -> Introduced in Charmcraft 1.3 - -When working locally with Charmcraft, the developer will use `charmcraft login` to get authentication tokens from Charmhub, which would be stored in the local keyring and used on all operations that need them. - -This is fine for local environments, but for remote ones (e.g. a CI/CD system) on one hand it's desirable to not login using the standard method (a browser opening an authentication web page to insert user, password, and *2FA*), and on the other hand the authentication tokens should be limited in different ways. - -The alternative login method is implemented through the `CHARMCRAFT_AUTH` environment variable, which needs to be set to useful credentials (which are obtained using the `--export` option, see below). - -If that variable is set Charmcraft will use it for all operations that needs authentication against Charmhub. Note that in this case the `login` and `logout` commands can not be used. - -To obtain credentials to be used in `CHARMCRAFT_AUTH`, the `login` command has the `--export` option, which accepts a file path. If specified, it will override the regular behaviour of storing the credentials in the user's keyring, and those will be exported to the given file path. The content of this file is what should be used verbatim in the environment variable. - -As mentioned at the beginning, it's also a good idea to use restricted credentials in a remote system. For this situation, the Charmcraft's `login` command provides different options to attenuate the obtained authentication tokens: - -- `--charm`: the charm name on which the permission will apply (can be specified multiple times) -- `--bundle`: the bundle name on which the permission will apply (can be specified multiple times) -- `--permission`: what action can be done on the specified package(s) (see below for a list; can be specified multiple times) -- `--channel`: the channel on which the package can be released (can be specified multiple times) -- `--ttl`: the time, in seconds, that the granted token will be useful (defaults to 30 days) - -All these indications are optional, and default to no restrictions applied on each category (except indicated the time-to-live, as indicated above). Note also that these restrictions can only be used if the credentials are exported to a file with the `--export` option. - -The available permissions are: -- `account-register-package`: register/request a new package name -- `account-view-packages`: list packages owned by the account or for which this account has collaborator rights -- `package-manage`: meta permission that includes all the `package-manage-*` ones -- `package-manage-acl`: add/invite/remove collaborators -- `package-manage-metadata`: edit metadata, add/remove media, etc. -- `package-manage-releases`: release revisions and close channels -- `package-manage-revisions`: upload new blobs, check for upload status -- `package-view`: meta permission that includes all the `package-view-*` ones -- `package-view-acl`: list the collaborators for a package, return privacy settings -- `package-view-metadata`: view the metadata for a package, including media -- `package-view-metrics`: view the metrics of a package -- `package-view-releases`: list the current releases (channel map) and the history of releases for a package -- `package-view-revisions`: list the existing revisions for a package, along with status information - -So, an example sequence for requesting/using credentials to set up a CI/CD system that will push and release a charm could be: - -- get the specific credentials in a file: - ```bash - $ charmcraft login --export=secrets.auth --charm=my-super-charm --permission=package-manage --channel=edge --ttl=2592000 - Login successful. Credentials exported to 'secrets.auth'. - ``` -- test that all is fine; for this get the content: - ```bash - $ CHARMCRAFT_AUTH=`cat test1` charmcraft whoami - name: J. Doe - username: jdoe-superdev - id: VTLZAToLcdaIPtisVBjfiQYCXbpKwbCc - charms: - - my-super-charm - permissions: - - package-manage - channels: - - edge - time to live (s): 2592000 - ``` -- to use this authorization token on a CI/CD system set the environment variable CHARMCRAFT_AUTH with the content of `secrets.auth` file, and use Charmcraft as normal: - - ```bash - export CHARMCRAFT_AUTH= - ... - charmcraft upload my-super-charm.charm --release edge - ``` - -## Check currently logged in user - -To check the currently logged in user, run `charmcraft whoami`. - -> See more: {ref}`ref_commands_whoami` - -## Log out of Charmhub - -To log out of Charmhub, run `charmcraft logout`. - -> See more: {ref}`ref_commands_logout` diff --git a/docs/howto/manage-tracks.md b/docs/howto/manage-tracks.md deleted file mode 100644 index d26b910a0..000000000 --- a/docs/howto/manage-tracks.md +++ /dev/null @@ -1,101 +0,0 @@ -(manage-tracks)= -# How to manage tracks - -> See also: {ref}`track` - -When you register a charm name on Charmhub, you automatically get 4 channels, all with track `latest`. However, as your charm evolves, you'll likely want to customise the shape of this track (e.g., to align with the workload) and then create new tracks in the new pattern. This document shows you how. - -(request-a-track-guardrail)= -## Request a track guardrail -> See also: {ref}`guardrail` - -To request a track guardrail, contact a Charmhub admin by creating a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . - - -(create-a-track)= -## Create a track - -Once you've requested a track guardrail, there are two ways to create a new track for your charm -- you can keep contacting a Charmhub admin every time or you can self-service. For most cases the latter option is likely to be more convenient and faster. - -### Ask a Charmhub admin - -To create a new track by contacting a Charmhub admin, create a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . The admin will create the new track that fits within the track guardrail you’ve set up for your charm. - -### Create it yourself - -To create a new track yourself, follow the steps below: - -```{important} - -As you might notice, this path is currently a little hacky. In the long-term it should become a lot smoother as there are plans to support it through the Charmcraft CLI. - -``` - -```{important} - -As you will see, this method currently relies on `charmcraft`+ `curl`. We recommend the Charmcraft bit because Charmcraft already understands the authentication mechanism used by Charmhub and can generate a suitable authentication token (macaroon) that will make it possible to then use `curl` directly to interact with the Charmhub API. This method also has the advantage that it can be adapted to use any HTTP client or library as long as it can pass custom headers. - -``` - - -**1. Enable `curl` access to the Charmhub API.** - -First, install `curl` and `jq`. - -```{important} - -You might already have both. - -``` - -Then, use Charmcraft to log in to Charmhub and export your Charmhub credentials / token (macaroon) to a file: - -```text -charmcraft login --export charmhub-creds.dat -``` - -Next, decode and extract the macaroon from the .dat file and place it in a header in an environment variable: - -```text -export CHARMHUB_MACAROON_HEADER="Authorization: Macaroon $(cat charmhub-creds.dat | base64 -d | jq -r .v)" -``` - -At this point you can use this variable in `curl` commands -- just make sure to specify the correct `Content-Type`. - -**2. Use `curl` to view the existing guardrails and tracks.** To view the guardrails and tracks associated with your charm, issue an HTTP `GET` request to `/v1//`. For example, for a charm named `hello-world-charm`: - -```text -curl https://api.charmhub.io/v1/charm/hello-world-charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -``` - -The guardrails and tracks of the package will be under the `track-guardrails` and `tracks` keys of `metadata`. Now you know what the new track may look like. - -> See more: [Charmhub API docs > `package_metadata`](https://api.charmhub.io/docs/default.html#package_metadata) - -````{important} - -**If you want to view the guardrails and tracks for *all* published charms:** Issue an HTTP `GET` request to `/v1/`, as below: - -```text -curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -``` - -> See more: [Charmhub API docs > `list_registered_names`](https://api.charmhub.io/docs/default.html#list_registered_names) - - -```` - - -**3. Use `curl` to create a new track.** Finally, to create a new track for your charm, issue an HTTP `POST` request to `/v1///tracks`, where `name` and `namespace` refer to the name and type of the package respectively. For example, given a charm named `hello-world-charm`, one can create two tracks `v.1` and `v.2` as follows: - -```text -curl https://api.charmhub.io/v1/charm/hello-world-charm/tracks -X POST -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -d '[{"name": "v.1"}, {"name": "v.2"}]' -``` - -Of course, the tracks must conform to the existing guardrail for the charm. - - -> See more: [Charmhub API docs > `create_tracks`](https://api.charmhub.io/docs/default.html#create_tracks) - - -That's it, you now have a new track for your charm! diff --git a/docs/howto/misc/index.md b/docs/howto/misc/index.md deleted file mode 100644 index 37fd765c0..000000000 --- a/docs/howto/misc/index.md +++ /dev/null @@ -1,16 +0,0 @@ -(howto-misc)= -# Miscellaneous - -```{toctree} -:maxdepth: 2 - -Migrate to `poetry` -Migrate to `python` -Cache intermediate build artefacts -Pack a hook-based charm with `charmcraft` -Pack a reactive-based charm with `charmcraft` -``` - - diff --git a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md deleted file mode 100644 index 82cd53096..000000000 --- a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md +++ /dev/null @@ -1,158 +0,0 @@ -(pack-a-hooks-based-charm-with-charmcraft.md)= -# How to pack a hooks-based charm with Charmcraft - -> See also: -> - {ref}`How to set up a charm project ` -> - {ref}`About charm types, by creation type ` -> - {ref}`How to turn a hooks-based charm into an ops charm ` - -You want a hooks-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. - -```{note} - - Introduced in Charmcraft 1.4 - -``` - - - - -Suppose you have a hooks-only charm, for example, [tiny-bash](https://github.com/erik78se/tiny-bash), which you can obtain as follows: - - -```text -$ git clone https://github.com/erik78se/tiny-bash -``` - - - -To make it packable by Charmcraft, all you need to do is navigate inside the charm directory and create a `charmcraft.yaml` file with the part definition for a hooks-based charm, as shown below: - - -```yaml -type: charm - -bases: - - build-on: - - name: "ubuntu" - channel: "20.04" - run-on: - - name: "ubuntu" - channel: "20.04" - -parts: - tiny-bash: - plugin: dump - source: . - prime: - - LICENSE - - README.md - - config.yaml - - copyright - - hooks - - icon.svg - - metadata.yaml -``` - - - -After this, you can pack your charm with `charmcraft pack`, as usual: - -```text -$ charmcraft pack -Charms packed: - tiny-bash_ubuntu-20.04-amd64.charm -``` - -If successful, the result should look as below, i.e., the charm file should contain all the files listed in the `prime` section of the `tiny-bash` part and the charm manifest. - -```shell -$ unzip -l tiny-bash_ubuntu-20.04-amd64.charm -Archive: tiny-bash_ubuntu-20.04-amd64.charm - Length Date Time Name ---------- ---------- ----- ---- - 423 2021-11-12 19:37 metadata.yaml - 431 2021-11-12 19:37 README.md - 12 2021-11-12 19:37 config.yaml - 3693 2021-11-12 19:37 icon.svg - 38 2021-11-12 19:37 copyright - 261 2021-11-12 20:08 manifest.yaml - 34523 2021-11-12 19:37 LICENSE - 381 2021-11-12 19:37 hooks/update-status - 346 2021-11-12 19:37 hooks/start - 1294 2021-11-12 19:37 hooks/shared-fs-relation-changed - 563 2021-11-12 19:37 hooks/stop - 497 2021-11-12 19:37 hooks/leader-elected - 447 2021-11-12 19:37 hooks/install - 417 2021-11-12 19:37 hooks/leader-settings-changed - 811 2021-11-12 19:37 hooks/upgrade-charm - 625 2021-11-12 19:37 hooks/config-changed ---------- ------- - 44762 16 files -``` - - - -And you can also deploy your application with `juju deploy`, as usual: - -```shell -$ juju deploy ./tiny-bash_ubuntu-20.04-amd64.charm -Located local charm "tiny-bash", revision 0 -Deploying "tiny-bash" from local charm "tiny-bash", revision 0 -``` -If successful, the result should look as below, i.e., with the application status active. - -```text -$ juju status -Model Controller Cloud/Region Version SLA Timestamp -default localhost-localhost localhost/localhost 2.9.12 unsupported 17:23:23-03:00 - -App Version Status Scale Charm Store Channel Rev OS Message -tiny-bash active 1 tiny-bash local 0 ubuntu update-status ran: 20:22 - -Unit Workload Agent Machine Public address Ports Message -tiny-bash/0* active idle 0 10.2.17.31 update-status ran: 20:22 - -Machine State DNS Inst id Series AZ Message -0 started 10.2.17.31 juju-55481c-0 focal Running -``` - - - - diff --git a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md deleted file mode 100644 index 29b5a0071..000000000 --- a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md +++ /dev/null @@ -1,126 +0,0 @@ -(pack-a-reactive-based-charm-with-charmcraft)= -# How to pack a reactive-based charm with Charmcraft - -> See also: -> - {ref}`How to set up a charm project ` -> - {ref}`How to pack your charm using Charmcraft ` -> - {ref}`About charm types, by creation type ` - - -Suppose you want a reactive-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. - -```{note} - - Introduced in Charmcraft 1.4. - -``` -```{note} - -The reactive way to write a charm represents an old standard. The recommended way to create a charm now is using {ref}`Charmcraft ` and {ref}`Ops `. - -``` - -To pack a reactive-based charm with Charmcraft, in the charm directory create a `charmcraft.yaml` file with the part definition for a reactive-based charm: - -```yaml -type: "charm" -bases: - - build-on: - - name: "ubuntu" - channel: "20.04" - run-on: - - name: "ubuntu" - channel: "20.04" -parts: - charm: - source: . - plugin: reactive - build-snaps: [charm] -``` - -Done. Now you can go ahead and pack your reactive-based charm with Charmcraft in the usual way using `charmcraft pack`. - - diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 5d41194ce..000000000 --- a/docs/index.md +++ /dev/null @@ -1,81 +0,0 @@ -# Charmcraft (`charmcraft`) - -```{toctree} -:maxdepth: 2 -:hidden: true - -tutorial/index -howto/index -reference/index -explanation/index -``` - -Charmcraft (`charmcraft`) is a tool designed to simplify the creation, building, and sharing of a [`juju` charm](https://juju.is/docs/juju/charmed-operator). - -When you initialise a charm with the `charmcraft` CLI, you automatically get all the crucial project files, pre-populated with helpful template content. These files are such that they can be `charmcraft`-packed right away; however, to make them meaningul for the application you are charming, you'll want to customise the YAML and [`ops`](https://juju.is/docs/sdk/ops)-powered Python in these files. For certain types of applications (Django, FastAPI, Flask, Go), if you initialise with a suitable `charmcraft` extension, things are even easier -- just tweak a few values in the YAML and you get a fully functioning charm. Either way, once you're pleased with what you've got, you can again use `chamcraft` to publish your charm on [Charmhub](https://charmhub.io/). - -You can create, build, and share a charm any way you want, but with `charmcraft` you get state-of-the-art results in record time. - -If you're a charm author, you *must* use `charmcraft`! - - ---------- - -## In this documentation - -````{grid} 1 1 2 2 - -```{grid-item-card} [Tutorial](/index) -:link: tutorial/index -:link-type: doc - -**Start here**: a hands-on introduction to Example Product for new users -``` - -```{grid-item-card} [How-to guides](/index) -:link: howto/index -:link-type: doc - -**Step-by-step guides** covering key operations and common tasks -``` - -```` - -````{grid} 1 1 2 2 -:reverse: - -```{grid-item-card} [Reference](/index) -:link: reference/index -:link-type: doc - -**Technical information** - specifications, APIs, architecture -``` - -```{grid-item-card} [Explanation](/index) -:link: explanation/index -:link-type: doc - -**Discussion and clarification** of key topics -``` - -```` - ---------- - - -## Project and community - -Charmcraft is a member of the Canonical family. It's an open source project -that warmly welcomes community projects, contributions, suggestions, fixes -and constructive feedback. - -* [Ubuntu Code of Conduct](https://ubuntu.com/community/code-of-conduct). -* [Canonical contributor licenses agreement](https://ubuntu.com/legal/contributors). - - From 7e7b1d69bf25ca8fa76684de312e555300715b16 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 11:44:00 -0500 Subject: [PATCH 11/19] docs: fix build breakage --- docs/index.md | 81 + docs/reference/charmcraft-cli.md | 7 - docs/reference/files/file-charmcraft-yaml.md | 1357 ----------------- docs/reference/files/file-contributing-md.rst | 6 + docs/tutorial/index.md | 19 - docs/tutorial/index.rst | 18 +- 6 files changed, 101 insertions(+), 1387 deletions(-) create mode 100644 docs/index.md delete mode 100644 docs/reference/charmcraft-cli.md delete mode 100644 docs/reference/files/file-charmcraft-yaml.md create mode 100644 docs/reference/files/file-contributing-md.rst delete mode 100644 docs/tutorial/index.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..5d41194ce --- /dev/null +++ b/docs/index.md @@ -0,0 +1,81 @@ +# Charmcraft (`charmcraft`) + +```{toctree} +:maxdepth: 2 +:hidden: true + +tutorial/index +howto/index +reference/index +explanation/index +``` + +Charmcraft (`charmcraft`) is a tool designed to simplify the creation, building, and sharing of a [`juju` charm](https://juju.is/docs/juju/charmed-operator). + +When you initialise a charm with the `charmcraft` CLI, you automatically get all the crucial project files, pre-populated with helpful template content. These files are such that they can be `charmcraft`-packed right away; however, to make them meaningul for the application you are charming, you'll want to customise the YAML and [`ops`](https://juju.is/docs/sdk/ops)-powered Python in these files. For certain types of applications (Django, FastAPI, Flask, Go), if you initialise with a suitable `charmcraft` extension, things are even easier -- just tweak a few values in the YAML and you get a fully functioning charm. Either way, once you're pleased with what you've got, you can again use `chamcraft` to publish your charm on [Charmhub](https://charmhub.io/). + +You can create, build, and share a charm any way you want, but with `charmcraft` you get state-of-the-art results in record time. + +If you're a charm author, you *must* use `charmcraft`! + + +--------- + +## In this documentation + +````{grid} 1 1 2 2 + +```{grid-item-card} [Tutorial](/index) +:link: tutorial/index +:link-type: doc + +**Start here**: a hands-on introduction to Example Product for new users +``` + +```{grid-item-card} [How-to guides](/index) +:link: howto/index +:link-type: doc + +**Step-by-step guides** covering key operations and common tasks +``` + +```` + +````{grid} 1 1 2 2 +:reverse: + +```{grid-item-card} [Reference](/index) +:link: reference/index +:link-type: doc + +**Technical information** - specifications, APIs, architecture +``` + +```{grid-item-card} [Explanation](/index) +:link: explanation/index +:link-type: doc + +**Discussion and clarification** of key topics +``` + +```` + +--------- + + +## Project and community + +Charmcraft is a member of the Canonical family. It's an open source project +that warmly welcomes community projects, contributions, suggestions, fixes +and constructive feedback. + +* [Ubuntu Code of Conduct](https://ubuntu.com/community/code-of-conduct). +* [Canonical contributor licenses agreement](https://ubuntu.com/legal/contributors). + + diff --git a/docs/reference/charmcraft-cli.md b/docs/reference/charmcraft-cli.md deleted file mode 100644 index a5f6a3605..000000000 --- a/docs/reference/charmcraft-cli.md +++ /dev/null @@ -1,7 +0,0 @@ -(charmcraft-cli)= -# `charmcraft` CLI - -```{toctree} -:maxdepth:2 -list-of-charmcraft-cli-commands -``` diff --git a/docs/reference/files/file-charmcraft-yaml.md b/docs/reference/files/file-charmcraft-yaml.md deleted file mode 100644 index 946307446..000000000 --- a/docs/reference/files/file-charmcraft-yaml.md +++ /dev/null @@ -1,1357 +0,0 @@ -(file-charmcraft-yaml)= -# File `charmcraft.yaml` - -```{toctree} -:maxdepth: 2 -:hidden: true - -/reference/models/index -``` - - - - - -> {ref}`List of files in the charm project ` > `charmcraft.yaml` -> -> [Source](https://github.com/canonical/charmcraft/blob/4f9aa82b5e36335301d8bb81b74e46e8c7ae5762/charmcraft/models/charmcraft.py#L106) -> -> See also: {ref}`How to configure Charmcraft ` - -```{important} - -Starting with Charmcraft 2.5, the `charmcraft.yaml` file is the only `yaml` file generated by `charmcraft init` and the only `yaml` file in a charm project that a charm author is supposed to edit directly. - -(`charmcraft pack` will use the information you provide here to generate the {ref}``actions.yaml` `, {ref}``config.yaml` `, and {ref}``metadata.yaml` `, as well as all the other files it usually does.) - -``` - - -The `charmcraft.yaml` file is a file in your charm project that contains keys that allow you to declare various types of information about the project (type, name, summary, etc.; how it should build, and what actions, configurations, integrations, etc.) in a form that can be used by Charmcraft, Charmhub, and Juju. - -```{important} - -If you're starting from an empty file, the only required key is the [`type`](#heading--type) key. However, depending on what value you set it to (`charm` or `bundle`), other keys become required as well. - -``` - - -````{dropdown} Expand to view the full spec with sample content all at once - - -```text -#This is intentionally designed to be a fairly complex charm definition. It makes use -# of every available keyword in charmcraft.yaml, expressing as much as possible. - -type: charm -name: full-charm -summary: A fully-defined charm, making use of all the available charm keywords. -description: | - This is intentionally designed to be a fairly complex charm definition. It makes use - of every available keyword in charmcraft.yaml, expressing as much as possible. -analysis: - ignore: - attributes: - - framework - linters: - - entrypoint -charmhub: - api-url: https://api.staging.charmhub.io - storage-url: https://storage.staging.snapcraftcontent.com -parts: - im-not-calling-this-what-you-expect: - plugin: charm - source: . - charm-entrypoint: src/charm.py - charm-binary-python-packages: [] - charm-python-packages: [] - charm-requirements: [] - charm-strict-dependencies: false - another-part: - plugin: nil -bases: -- build-on: - - name: ubuntu - channel: '22.04' - architectures: - - amd64 - - riscv64 - - name: ubuntu - channel: '20.04' - architectures: - - amd64 - - arm64 - run-on: - - name: ubuntu - channel: '22.04' - architectures: - - amd64 - - name: ubuntu - channel: '22.04' - architectures: - - riscv64 - - name: ubuntu - channel: '22.04' - architectures: - - arm64 -- build-on: - - name: ubuntu - channel: '24.04' - run-on: - - name: ubuntu - channel: '24.04' - architectures: - - amd64 - - arm64 - - riscv64 - - s390x - - ppc64el - - armhf -actions: - snapshot: - description: Take a snapshot of the database. - params: - filename: - type: string - description: The name of the snapshot file. - compression: - type: object - description: The type of compression to use. - properties: - kind: - type: string - enum: - - gzip - - bzip2 - - xz - quality: - description: Compression quality - type: integer - minimum: 0 - maximum: 9 - required: - - my-favourite-photo.tiff - additionalProperties: false -assumes: -- any-of: - - juju >= 2.9 - - all-of: - - juju >= 3.0 - - juju < 4.0 -- k8s-api -containers: - super-app: - resource: super-app-image - mounts: - - storage: logs - location: /logs -devices: - super-cool-gpu: - type: amd.com/gpu - description: Some sweet AMD GPU - countmin: 69 - countmax: 420 - lame-gpu: - type: nvidia.com/gpu - description: A GPU I regret buying - countmin: 0 - countmax: 1 -extra-bindings: - Ring of Power: null -peers: - friend: - interface: life - limit: 150 - optional: true - scope: container -provides: - self: - interface: identity -requires: - parent: - interface: birth - limit: 2 - optional: false - scope: global -resources: - water: - type: file - filename: /dev/h2o - super-app-image: - type: oci-image - description: OCI image for the Super App (hub.docker.com/_/super-app) -storage: - jbod: - type: block - description: A nice block storage for me to use as swap space - shared: false - properties: - - transient -subordinate: false -terms: -- Butterscotch is regal -- Cara is adorable -links: - contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont - documentation: https://juju.is/docs/sdk/charmcraft-yaml - issues: - - https://launchpad.net/~charmcraft-team - - https://github.com/canonical/charmcraft/issues - source: - - https://github.com/canonical/charmcraft - website: - - https://snapcraft.io/charmcraft -config: - options: - name: - default: Wiki - description: The name, or Title of the Wiki - type: string - skin: - default: vector - description: skin for the Wiki - type: string - logo: - description: URL to fetch logo from - type: string - admins: - description: 'Comma-separated list of admin users to create: user:pass[,user:pass]+' - type: string - debug: - default: false - type: boolean - description: turn on debugging features of mediawiki -``` - -```` - - -Read on to find out more about each key. - - - - -(file-charmcraft-yaml-actions)= -## `actions` -> Owned by `juju`. Used by `charmcraft`, `charmhub`, `juju`. - - -**Status:** Optional. - -**Purpose:** Defines an action. - -**Name:** String = user-defined action name. - -**Value:** Mapping = the YAML equivalent of a JSON Schema object, except: - -1. It includes some new keys specific to actions: `description`, `parallel`, and `execution-group`. -2. It does not currently support the JSON Schema concepts `$schema` and `$ref`. -3. The `additionalProperties` and `required` keys from JSON Schema can be used at the top-level of an action (adjacent to `description` and `params`), but also used anywhere within a nested schema. - -> See more: [JSON Schema Docs](https://www.learnjsonschema.com/2020-12/) - -## `actions..description` - -**Status:** Optional. - -**Purpose:** Describes the action. - -**Value:** String = the action description. - - - -## `actions..execution-group` - - -**Status:** Optional, defaults to “”. - -**Purpose:** Holds action task execution groups. - -**Value:** String = the execution group in which to place tasks created by this action. - -> See more: [Juju | `juju run`](https://juju.is/docs/juju/juju-run), [Juju | Task](https://juju.is/docs/juju/task) - -## `actions..parallel` - - -**Status:** Optional, defaults to false. - -**Purpose:** Sets whether to allow tasks created by this action to execute in parallel. - -**Value:** Boolean. - -> See more: [Juju | `juju run`](https://juju.is/docs/juju/juju-run), [Juju | Task](https://juju.is/docs/juju/task) - -## `actions..params` - -**Status:** Optional. - -**Purpose:** Holds action parameters. - -**Value:** Mapping. Keys are parameter names. - - The entire map of `actions..params` is inserted into the action schema object as a “properties” validation keyword. The Juju CLI may read the “description” annotation keyword of each parameter to present to the user when describing the action. - - -## `actions..params.` - -**Status:** Optional. - -**Purpose:** Defines an action parameter. - -**Name:** The name of the action parameter. - -**Value:** Mapping = the YAML equivalent of a JSON Schema object. - -> See more: [JSON Schema Docs](https://www.learnjsonschema.com/2020-12/) - - -(file-charmcraft-yaml-analysis)= -## `analysis` -> Owned by `charmcraft`. Used by `charmcraft`. - -**Status:** Optional. - -**Purpose:** Defines how the analysis done on the package will behave. This analysis is run implicitly as part of the `pack` command but can be called explicitly with the `charmcraft analyze` command. - - - -**Structure:** So far the only thing that can be configured is which attributes or linters will be ignored. - -```text -analysis: - ignore: - attributes: [,...] - linters: [,...] -``` - -**Example:** - -```text -analysis: - ignore: - attributes: - - framework - linters: - - entrypoint -``` - -(file-charmcraft-yaml-assumes)= -## `assumes` - -**Status:** Optional. Recommended for Kubernetes charms. - -**Purpose:** Allows charm authors to explicitly state in the metadata of a charm various features that a Juju model must be able to provide to ensure that the charm can be successfully deployed on it. When a charm comes preloaded with such requirements, this enables Juju to perform a pre-deployment check and to display user-friendly error messages if a feature requirement cannot be met by the model that the user is trying to deploy the charm to. If the assumes section of the charm metadata is omitted, Juju will make a best-effort attempt to deploy the charm, and users must rely on the output of `juju status` to figure out whether the deployment was successful. The `assumes` key is available since 2.9.23. - -**Structure:** The key consists of a list of features that can be given either directly or, depending on the complexity of the condition you want to enforce, nested under one or both of the boolean expressions `any-of` or `all-of`, as shown below. In order for a charm to be deployed, all entries in the `assumes` block must be satisfied. - -```yaml -assumes: - - - - any-of: - - - - - - all-of: - - - - -``` - - The supported feature names are as below: - -|||| -|- | - | - | -|`juju `

E.g., `juju < 3.0`.
E.g., `juju >= 2.9` | The charm deploys iff the model runs agent binaries with the specified Juju version(s). |Since 2.9.23| -|`k8s-api` | The charm deploys iff the underlying substrate for the model is Kubernetes. |Since 2.9.23| - -The Boolean expressions are defined as below: - -||| -|-|-| -|`any-of`| The sub-expression is satisfied if any of the provided child expressions is satisfied.| -|`all-of` | The sub-expression is satisfied if all of the provided child expressions are satisfied.| - -**Examples:** - - -````{dropdown} Expand to see a simple example - - -```yaml -assumes: - - juju >= 2.9.23 - - k8s-api -``` - -```` - - -````{dropdown} Expand to see an example with a nested expression - - -```yaml -assumes: -- any-of: - - juju >= 2.9 - - all-of: - - juju >= 3.0 - - juju < 4.0 -- k8s-api -``` - -```` - -(file-charmcraft-yaml-bases)= -## `bases` - -`````{note} - -`bases` is replaced by `base` , `build-base`, and `platforms`. - - - -````{dropdown} See more - - -```yaml -# The run time base, the base format is @, -# accepted bases are: -# - ubuntu@24.04 -base: -# The build time base, if not defined the base is also the build time -# base, in addition to valid bases, the build-base can be "devel" -# which would use the latest in development Ubuntu Series. -build-base: - -platforms: - # The supported platforms, may omit build-for if platform-name - # is a valid arch, valid architectures follow the Debian architecture names, - # accepted architectures are: - # - amd64 - # - arm64 - # - armhf - # - ppc64el - # - riscv64 - # - s390x - : - # The build time architecture - build-on: | - # The run time architecture - build-for: | - -``` - -```` - -````` - -**Status:** If the [`type`](#heading--type) key is set to `charm`, required. (If the `type` key is set to `bundle`, leads to an error.) - -**Purpose:** Specifies a list of environments (OS version and architecture) where the charm must be built on and run on. - -When packing in "destructive mode", the base(s) that match(es) the current environment will be used, otherwise an instance will be requested to LXD or Multipass for each specified base to pack in that environment. - - -**Structure:** This key supports a list of bases where the charm can be built, and where that build can run. Each item can be expressed using two different internal structures, a short and a long form. The long one is more explicit: - -```yaml -bases: - - build-on: - - name: - channel: - architectures: - - - run-on: - - name: - channel: - architectures: - - -``` - -The `run-on` part of each `build-on` is optional, and defaults to what's specified in the corresponding 'build-on'. And in both structures the list of architecture strings is also optional, defaulting to the machine architecture. - -The short form is more concise and simple (at the cost of being less flexible): - -```yaml -bases: - - name: - channel: - architectures: - - -```` - -It implies that the specified base is to be used for both `build-on` and `run-on`. And as above, the list of architecture strings is also optional, defaulting to the machine architecture. - -Be sure to check [this detailed documentation](https://discourse.charmhub.io/t/charmcraft-bases-provider-support/4713) for more information and the different possibilities of these structures, including several examples. - - -**Example:** - -```text -bases: -- build-on: - - name: ubuntu - channel: '22.04' - architectures: - - amd64 - - riscv64 - - name: ubuntu - channel: '20.04' - architectures: - - amd64 - - arm64 - run-on: - - name: ubuntu - channel: '22.04' - architectures: - - amd64 - - name: ubuntu - channel: '22.04' - architectures: - - riscv64 - - name: ubuntu - channel: '22.04' - architectures: - - arm64 -- build-on: - - name: ubuntu - channel: '24.04' - run-on: - - name: ubuntu - channel: '24.04' - architectures: - - amd64 - - arm64 - - riscv64 - - s390x - - ppc64el - - armhf -``` - - -(file-charmcraft-yaml-charm-libs)= -## `charm-libs` - -**Status:** Optional. - -**Purpose:** Declares charm libraries for Charmcraft to include in the charm project. For each lib, make sure to include both the lib name (in `.` format) and the lib version (in `"[.]"` format). For example: - -```yaml -charm-libs: - # Fetch postgres_client lib with API version 1 and latest patch: - - lib: postgresql.postgres_client - version: "1" - - # Fetch mysql lib with API version 0 and patch version 5: - - lib: mysql.mysql - version: "0.5" -``` - -(file-charmcraft-yaml-charmhub)= -## `charmhub` - - -```{note} - -In Charmcraft 3.0 and up, these keys will no longer be valid in `charmcraft.yaml`. Use the environment variables `CHARMCRAFT_STORE_API_URL`, `CHARMCRAFT_UPLOAD_URL` and `CHARMCRAFT_REGISTRY_URL` instead. - -``` - -**Status:** Optional. - -**Purpose:** Configures Charmcraft's interaction with store servers. - -**Structure:** This key allows for the configuration of two values---the base URL for the Charmhub API and the base URL to push binaries to Charmhub. These keys are also optional. - -``` -charmhub: - api-url: - storage-url: - registry-url: -``` - -The key is used mostly in the context of "private" charm stores, defaulting to the standard Canonical services to operate with charms. - -**Example:** - -```text -charmhub: - api-url: https://api.staging.charmhub.io - storage-url: https://storage.staging.snapcraftcontent.com -``` - -(file-charmcraft-yaml-config)= -## `config` - -> See first: [Juju | Application configuration](https://juju.is/docs/juju/configuration) - -**Status:** Optional. - -**Purpose:** The `config` key allows you to create configuration options for your charm. - - -```yaml -config: - options: - # Each option name is the name by which the charm will query the option. -
- # (Optional): A string describing the option. Also appears on charmhub.io - description: -``` - -For the case where the `type` is `secret`: This is a string that needs to correspond to the secret URI. - -**Example:** - -````{dropdown} Expand to see an example with types string and boolean - -```text -config: - options: - name: - default: Wiki - description: The name, or Title of the Wiki - type: string - logo: - description: URL to fetch logo from - type: string - admins: - description: 'Comma-separated list of admin users to create: user:pass[,user:pass]+' - type: string - debug: - default: false - type: boolean - description: turn on debugging features of mediawiki - port: - default: 80 - type: int - description: port on which to serve the wiki - timeout: - default: 60.0 - type: float - description: maximum time before rendering a page will fail - certificate: - type: secret - description: TLS certificate to use for securing connections -``` -```` - -(file-charmcraft-yaml-containers)= -## `containers` - -**Status:** Required for Kubernetes charms (except for proxy charms running on Kubernetes). - -**Purpose:** The `containers` key allows you to define a map of containers to be created adjacent to the charm (as a sidecar, in the same pod). - -**Structure:** This key consists of a list of containers along with their specification. Each container can be specified in terms of `resource`, `bases`, `uid`, `gid` and `mounts`, where one of either the `resource` or the `bases` subkeys must be defined, and `mounts` is optional. `resource` stands for the OCI image resource used to create the container; to use it, specify an OCI image resource name (that you will then define further in the [`resources`](#heading--resources) block). `bases` is a list of bases to be used for resolving a container image, in descending order of preference; to use it, specify a base name (for example, `ubuntu`, `centos`, `windows`, `osx`, `opensuse`), a [channel](https://snapcraft.io/docs/channels), and an architecture. `mounts` is a list of mounted storages for this container; to use it, specify the name of the storage to mount from the charm storage and, optionally, the location where to mount the storage. And, starting with Juju 3.5.0, `uid` and `gid` are the UID and, respectively, GID to run the Pebble entry process for this container as; they can be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved for users) and the default is 0 (root). - -```yaml -containers: - : - resource: - bases: - - name: - channel: - architectures: - - - mounts: - - storage: - location: - uid: - gid: -``` - - - -**Examples:** - -````{dropdown} Expand to see an example with resource and mounts - -```yaml -containers: - super-app: - resource: super-app-image - mounts: - - storage: logs - location: /logs -``` - -```` - - - - -(file-charmcraft-yaml-description)= -## `description` - - **Status:** If the [`type`](#heading--type) key is set to `charm`, required. - -**Example:** - -```text -description: | - A Juju-operated Traefik operator that routes requests from the outside of a - Kubernetes cluster to Juju units and applications. - -``` - -(file-charmcraft-yaml-devices)= -## `devices` - -**Status:** Optional - -**Purpose:** Defines the device requests for the charm, for example a GPU. - -**Structure:** - -```text -devices: - # Each key represents the name of the device - : - - # (Required) The type of device requested - type: gpu | nvidia.com/gpu | amd.com/gpu - - # (Optional) Description of the requested device - description: - - # (Optional) Minimum number of devices required - countmin: - - # (Optional) Maximum number of devices required - countmax: - -``` - -**Example:** - -```text -devices: - super-cool-gpu: - type: amd.com/gpu - description: Some sweet AMD GPU - countmin: 69 - countmax: 420 - lame-gpu: - type: nvidia.com/gpu - description: A GPU I regret buying - countmin: 0 - countmax: 1 -``` - -(file-charmcraft-yaml-extra-bindings)= -## `extra-bindings` - -**Status:** Optional. - -**Purpose:** Extra bindings for the charm. For example binding extra network interfaces. - -**Structure:** A key-only map; key represents the name of the binding: - -```text -extra-bindings: - : -``` - -**Example:** - -```text -extra-bindings: - Ring of Power: null -``` - -(file-charmcraft-yaml-links)= -## `links` - -**Status:** Optional. Recommended. - -**Purpose:** Links to various additional information, to be displayed on Charmhub. - -**Example:** - -```text -links: - contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont - - # Link to documentation cover page on Discourse: - documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 - - # Link to bug tracker: - issues: - - https://github.com/canonical/traefik-k8s-operator/issues - - # Link to source code: - source: - - https://github.com/canonical/traefik-k8s-operator - - # Link to charm project website outside of Charmhub, if available. - website: - - https://charmed-kubeflow.io/ -``` - -(file-charmcraft-yaml-name)= -## `name` - - **Status:** If the [`type`](#heading--type) key is set to `charm`, required. - -**Purpose:** The name of the charm. Determines the `.charm` file name and, if the charm is published on Charmhub, the charm page URL in Charmhub. As a result, it also determines the name administrators will ultimately use to deploy the charm. E.g. `juju deploy `. - -**Structure:** - -```yaml -name: -``` - -**Example:** - -```text -name: traefik-k8s -``` - -(file-charmcraft-yaml-parts)= -## `parts` - -**Status:** Optional. Only used by the `pack` command. - -**Purpose:** Configures the various mechanisms to obtain, process and prepare data from different sources that end up being a part of the final charm. It's optional, and only used by the `pack` command. - -**Value:** Map. Keys are user-defined part names. The value of each key is a map where keys are part properties. Regarding the `plugin` property: In addition to the standard set, Charmcraft provides 3 further plugins: `charm`, `bundle`, `reactive`. - -> See more: [Craft Parts | Part properties](https://canonical-craft-parts.readthedocs-hosted.com/en/latest/common/craft-parts/reference/part_properties.html) - -**Example:** - -```text -parts: - libs: - plugin: dump - source: /usr/local/lib/ - organize: - "libxxx.so*": lib/ - prime: - - lib/ -``` - -**Details:** - - - -A part is a declarative representation on how to add a source or component to the charm. It specifies the mechanisms to obtain, process and prepare individual subsets of the final artefact (each "part" of the "whole"). - -Parts have logic encoded in plugins: a plugin is what has the knowledge into how to transform a given source declared in a part into a usable artefact for a charm. - -The `parts` key is optional. If not included, it will default to a `charm` or `bundle` part (depending on the project type), which will use a `charm` or `bundle` plugin correspondingly. - -Those two plugins and the `reactive` one, all detailed below, are provided by Charmcraft itself and can be used by other custom parts. - -Other plugins are provided by the Craft Parts library (which is a Charmcraft dependency), check this [supported plugins](https://snapcraft.io/docs/supported-plugins) page. Furthermore, other plugins can be written if needed, refer to this [writing local plugins](https://snapcraft.io/docs/writing-local-plugins) documentation. - -Beyond plugins, Charmcraft gives the possibility of using the full power of the parts lifecycle, allowing to add parts, and also to override and customise steps of a part’s lifecycle (`pull`, `build`, `stage`, and `prime`) using shell scripts directly sourced from `charmcraft.yaml`, both for custom written parts and the ones included by Charmcraft. Please refer to the [Parts Lifecycle](https://snapcraft.io/docs/parts-lifecycle) documentation to learn more about this. - - - -### The `charm` plugin - -Used to pack a Charm that is based on the [Operator Framework](https://ops.readthedocs.io/en/latest/). - -Supports the following configuration: - -```text -parts: - my-charm: - plugin: charm - charm-entrypoint: - charm-requirements: - charm-python-packages: - charm-binary-python-packages: - prime: -``` - -In detail: - -- `charm-entrypoint`: The charm entry point, relative to the project directory. It is optional (new in charmcraft 1.2), if not defined defaults to `src/charm.py`. - -- `charm-requirements`: A list of requirements files specifying Python dependencies. It is optional (new in charmcraft 1.2); if not defined, defaults to a list with one `requirements.txt` entry if that file is present in the project directory. - -- `charm-python-packages`: A list of Python packages to install before installing requirements. These packages will be installed from sources and built locally at packing time. It is optional (new in charmcraft 1.4), defaults to empty. - -- `charm-binary-python-packages`: A list of python packages to install before installing requirements and regular Python packages. Binary packages are allowed, but they may also be installed from sources if a package is only available in source form. It is optional (new in charmcraft 1.4), defaults to empty. - -- `prime`: List of extra file and directory paths to include in the charm. Note that `bundle.yaml`, the entry point file and hooks are included automatically when packing a charm. Additionally, `config.yaml`, `metrics.yaml`, `actions.yaml`, `lxd-profile.yaml`, `templates`, `version`, `lib` and `mod` will be included if they exist in the project directory. It is optional. - - -### The `bundle` plugin - -Used to pack a [charm bundle](https://juju.is/docs/olm/bundles), a collection of charms which have been carefully combined and configured in order to automate a multi-charm solution. - -Supports the following configuration: - -```text -parts: - my-bundle: - prime: - plugin: bundle -``` - -In detail: - -- `prime`: List of extra file and directory paths to include in the bundle. Note that `bundle.yaml` and `README.md` are included automatically when packing a bundle. Optional. - - -### The `reactive` plugin - -Used to pack charms using the reactive framework. - -Note that this is a framework that has now been superseded by [the Ops library](https://github.com/canonical/operator), please use that framework instead of reactive. Support for reactive in Charmcraft is only to ease the transition of old charms into the new framework. - -Supports the following configuration: - -``` -parts: - charm: - source: . - plugin: reactive - build-snaps: [charm] - reactive-charm-build-arguments: -``` - -The `reactive_charm_build_arguments` allows to include extra command line arguments in the underlying `charm build` call. - - - -**Example:** - -```text -parts: - im-not-calling-this-what-you-expect: - plugin: charm - source: . - charm-entrypoint: src/charm.py - charm-binary-python-packages: [] - charm-python-packages: [] - charm-requirements: [] - charm-strict-dependencies: false - another-part: - plugin: nil -``` - - -(file-charmcraft-yaml-peers-provides-requires)= -## `peers`, `provides`, `requires` - -> See also: [Juju | Relation (integration)](https://juju.is/docs/juju/relation) - -````{dropdown} Expand to view an example featuring all three relation keys (peers, provides, or requires) - -```text -peers: - friend: - interface: life - limit: 150 - optional: true - scope: container -provides: - self: - interface: identity -requires: - parent: - interface: birth - limit: 2 - optional: false - scope: global -``` - - -```` - - -````{dropdown} Expand to view the full schema for a chosen endpoint role (peers, provides, or requires) - -``` -: # 'peers', 'provides', or 'requires' - # Each key represents the name of the endpoint as known by this charm - : - - # (Required) The interface schema that this relation conforms to - interface: - - # (Optional) Maximum number of supported connections to this relation - # endpoint. This field is an integer - limit: - - # (Optional) Defines if the relation is required. Informational only. - optional: true | false - - # (Optional) The scope of the relation. Defaults to "global" - scope: global | container -``` - -```` - -***``*** - -**Status:** If you want to define any kind of integration, required. - -**Purpose:** To define an integration endpoint. - -**Structure:** *Name:* Depending on what kind of an integration you are trying to define: `peers`, `provides`, or `requires`. *Type:* Map. *Value:* One or more key-value pairs denoting a relation and its associated properties. - -***`.`*** - -**Status:** Required. - -**Purpose:** To define the name of the relation as known by this charm. - -**Structure:** *Name: User-defined.* *Type:* string. *Value:* - -***`..interface`*** - -**Status:** Required. - -**Purpose:** To define the interface schema that this relation conforms to. - -**Structure:** *Type:* String. *Value:* The name of the interface. Usually defined by the author of the charm providing the interface. Cannot be `juju`. Cannot begin with `juju-`. Must only contain characters `a-z` and `-` and cannot start with `-`. :warning: The interface name is the only means of establishing whether two charms are compatible for integration; and carries with it nothing more than a mutual promise that the provider and requirer somehow know the communication protocol implied by the name. - -***`..limit`*** - -**Status:** Optional. - -**Purpose:** To define the maximum number of supported connections to this relation endpoint. - -**Structure:** *Type:* Integer. *Value:* User-defined. *Default value:* `nil`. - -***`..optional`*** - -**Status:** Optional. - -**Purpose:** To define if the relation is required. Informational only. - -**Structure:** *Type:* Boolean. *Possible values:* `true`, `false`. *Default value:* `false`. - -***`..scope`*** - -**Status:** Optional. - -**Purpose:** To define the scope of the relation, that is, the set of units from integrated applications that are reported to the unit as members of the integration. - -**Structure:** *Type:* String. **Possible values:** `container`, `global`. Container-scoped integrations are restricted to reporting details of a single principal unit to a single subordinate, and vice versa, while global integrations consider all possible remote units. Subordinate charms are only valid if they have at least one `requires` integration with `container` scope. *Default value:* `global`. - - -(file-charmcraft-yaml-resources)= -## `resources` - -> See first: [Juju | Charm resource](https://juju.is/docs/juju/charm-resource) - -**Status:** Optional. - -**Purpose:** To define a resource for your charm. Note: Kubernetes charms must declare an `oci-image` resource for each container they define in the [`containers`](#heading--containers) mapping. - -**Structure:** - - - -```text -# (Optional) Additional resources that accompany the charm -resources: - # Each key represents the name of a resource - # mentioned in the 'resource' subkey of the 'containers' key. - : - - # (Required) The type of the resource - type: file | oci-image - - # (Optional) Description of the resource and its purpose - description: - - # (Required: when type:file) The filename of the resource as it should - # appear in the filesystem - filename: -``` - -**Examples:** - - - -````{dropdown} Expand to see an example with a file resource - - -```text -resources: - water: - type: file - filename: /dev/h2o -``` - - -```` - -````{dropdown} Expand to see an example with an OCI-image resource - -```yaml -resources: - super-app-image: - type: oci-image - description: OCI image for the Super App (hub.docker.com/_/super-app) -``` - -```` - -(file-charmcraft-yaml-storage)= -## `storage` - -**Status:** Optional. - -**Purpose:** Storage requests for the charm. - -**Structure:** - -```yaml -storage: - # Each key represents the name of the storage - : - - # (Required) Type of the requested storage - # The filesystem type requests a directory in which the charm may store files. - # If the storage provider only supports provisioning disks, then a disk will be - # created, attached, partitiioned, and a filesystem created on top, and the - # filesystem will be presented to the charm as normal. - # The block type requests a raw block device, typically disks or logical volumes. - type: filesystem | block - - # (Optional) Description of the storage requested - description: - - # (Optional) The mount location for filesystem stores. For multi-stores - # the location acts as the parent directory for each mounted store. - location: - - # (Optional) Indicates if all units of the application share the storage - shared: true | false - - # (Optional) Indicates if the storage should be made read-only (where possible) - read-only: true | false - - # (Optional) The number of storage instances to be requested - multiple: - range: | - | - | + - - # (Optional) Minimum size of requested storage in forms G, GiB, GB. Size - # multipliers are M, G, T, P, E, Z or Y. With no multiplier supplied, M - # is implied. - minimum-size: | - - # (Optional) List of properties, only supported value is "transient" - properties: - - transient -``` - - -**Example:** - -```text -storage: - jbod: - type: block - description: A nice block storage for me to use as swap space - shared: false - properties: - - transient -``` - -(file-charmcraft-yaml-subordinate)= -## `subordinate` - -**Status:** Optional. - -**Purpose:** Configures whether the charm is meant to be deployed as a subordinate to a principal charm. - -**Structure:** - -```yaml -subordinate: true | false -``` - -**Example:** - -```text -subordinate: false -``` - -(file-charmcraft-yaml-summary)= -## `summary` - -**Status:** If the [`type`](#heading--type) key is set to `charm`, required. - -**Structure:** A short, one-line description of the charm. - -**Example:** - -```text -summary: | - A Juju charm to run a Traefik-powered ingress controller on Kubernetes. -``` - -(file-charmcraft-yaml-terms)= -## `terms` - -**Status:** Optional. - -**Purpose:** Lists the terms that any charm user agree to when they're using the charm. - -**Structure:** The list of terms: - -```yaml -terms: - - -``` - -**Example:** - -```text -terms: -- Butterscotch is regal -- Cara is adorable -``` - -(file-charmcraft-yaml-title)= -## `title` - -**Status:** Optional. - -**Purpose:** Defines the title of your charm on Charmhub. - -**Example:** - -```text -title: | - Traefik Ingress Operator for Kubernetes -``` - -(file-charmcraft-yaml-type)= -## `type` - -**Status:** Required. - -**Purpose:** Indicates the type of entity for which the present config exists. - -**Structure:** *Type:* String. *Value:* `charm` or `bundle`. - -**Example:** - -```text -type: charm -``` - -**Contributors:** @jose-mingorance , @lengau, @tmihoc, @lucabello diff --git a/docs/reference/files/file-contributing-md.rst b/docs/reference/files/file-contributing-md.rst new file mode 100644 index 000000000..738b3e7c3 --- /dev/null +++ b/docs/reference/files/file-contributing-md.rst @@ -0,0 +1,6 @@ +(file-contributing-md)= +# File 'CONTRIBUTING.md' + +> {ref}`List of files in the charm project ` > `CONTRIBUTING.md` + +TO BE ADDED diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md deleted file mode 100644 index 2d8da9968..000000000 --- a/docs/tutorial/index.md +++ /dev/null @@ -1,19 +0,0 @@ -(tutorial)= -# Tutorial - -Our tutorial comes in multiple flavours -- pick your flavour of choice! - -```{note} -Currently all our tutorial flavours demonstrate `charmcraft` in combination with an 12-factor app extension -- a journey that does not require any [`ops`](https://ops.readthedocs.io/en/latest/#) and which does not currently include publishing the charm. To get a sense of the workflow for other types of applications and including the publication step, see {ref}`manage-charms`. -``` - - -```{toctree} -:maxdepth: 2 - - -Write your first Kubernetes charm for a Django app -Write your first Kubernetes charm for a FastAPI app -Write your first Kubernetes charm for a Go app - -``` diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index 3965c1272..8932e9294 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -1,10 +1,20 @@ .. _tutorial: Tutorial -******** +======== -.. toctree:: - :maxdepth: 2 +Our tutorial comes in multiple flavours -- pick your flavour of choice! + +.. note:: - flask + Currently all our tutorial flavours demonstrate ``charmcraft`` in combination + with an 12-factor app extension -- a journey that does not require any + `ops `_ and which does not currently include publishing the charm. To get a sense of the workflow for other types of applications and including the publication step, see :ref:`manage-charms`. + +.. toctree:: + :maxdepth: 2 + Write your first Kubernetes charm for a Django app + flask + Write your first Kubernetes charm for a FastAPI app + Write your first Kubernetes charm for a Go app From 6f2f2ac992f031c5aad5d33f879779d79ea31c73 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 11:47:47 -0500 Subject: [PATCH 12/19] docs: migrate contributing.md reference to rst --- docs/reference/files/file-contributing-md.md | 6 ------ docs/reference/files/file-contributing-md.rst | 13 +++++++++---- 2 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 docs/reference/files/file-contributing-md.md diff --git a/docs/reference/files/file-contributing-md.md b/docs/reference/files/file-contributing-md.md deleted file mode 100644 index 738b3e7c3..000000000 --- a/docs/reference/files/file-contributing-md.md +++ /dev/null @@ -1,6 +0,0 @@ -(file-contributing-md)= -# File 'CONTRIBUTING.md' - -> {ref}`List of files in the charm project ` > `CONTRIBUTING.md` - -TO BE ADDED diff --git a/docs/reference/files/file-contributing-md.rst b/docs/reference/files/file-contributing-md.rst index 738b3e7c3..0e58478fc 100644 --- a/docs/reference/files/file-contributing-md.rst +++ b/docs/reference/files/file-contributing-md.rst @@ -1,6 +1,11 @@ -(file-contributing-md)= -# File 'CONTRIBUTING.md' +.. _file-contributing-md: -> {ref}`List of files in the charm project ` > `CONTRIBUTING.md` +``CONTRIBUTING.md`` +******************* -TO BE ADDED +This file contains instructions about how to contribute to your charm. It can contain +anything you want, but it is recommended that it have at least: + +- How to set up a development environment +- Code and test standards +- Instructions for submitting changes upstream. From f6aa440a2f638b7c93fcb3c4e78fe3358cea5038 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 12:02:24 -0500 Subject: [PATCH 13/19] docs: replace files index with rst --- docs/reference/files/index.md | 30 ------------------------------ docs/reference/files/index.rst | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 docs/reference/files/index.md create mode 100644 docs/reference/files/index.rst diff --git a/docs/reference/files/index.md b/docs/reference/files/index.md deleted file mode 100644 index 6cd6bb14e..000000000 --- a/docs/reference/files/index.md +++ /dev/null @@ -1,30 +0,0 @@ -(list-of-files-in-a-charm-or-a-bundle)= -# List of files in a charm or a bundle - - -```{toctree} -:maxdepth: 1 - - -file-bundle-yaml -file-actions-yaml -file-charmcraft-yaml -file-config-yaml -file-contributing-md -file-dispatch -file-icon-svg -file-libname-py -file-license -file-lxd-profile-yaml -file-manifest-yaml -file-metadata-yaml -file-pyproject-toml -file-readme-md -file-requirements-dev-txt -file-requirements-txt -file-src-charm-py -file-tests-unit-test-charm-py -file-tests-integration-test-charm-py -file-tox-ini -``` - diff --git a/docs/reference/files/index.rst b/docs/reference/files/index.rst new file mode 100644 index 000000000..9bd6f9aef --- /dev/null +++ b/docs/reference/files/index.rst @@ -0,0 +1,30 @@ +.. _list-of-files-in-a-charm-or-a-bundle: + +List of files in a charm or a bundle +==================================== + + +.. toctree:: + :maxdepth: 1 + + + file-bundle-yaml + file-actions-yaml + file-charmcraft-yaml + file-config-yaml + file-contributing-md + file-dispatch + file-icon-svg + file-libname-py + file-license + file-lxd-profile-yaml + file-manifest-yaml + file-metadata-yaml + file-pyproject-toml + file-readme-md + file-requirements-dev-txt + file-requirements-txt + file-src-charm-py + file-tests-unit-test-charm-py + file-tests-integration-test-charm-py + file-tox-ini From d8e2d7781870abeb6390dce260e23607a598763c Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 12:02:33 -0500 Subject: [PATCH 14/19] docs: move dispatch file to rst --- docs/reference/files/file-dispatch.md | 18 ------------- docs/reference/files/file-dispatch.rst | 37 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 18 deletions(-) delete mode 100644 docs/reference/files/file-dispatch.md create mode 100644 docs/reference/files/file-dispatch.rst diff --git a/docs/reference/files/file-dispatch.md b/docs/reference/files/file-dispatch.md deleted file mode 100644 index 477a4ee20..000000000 --- a/docs/reference/files/file-dispatch.md +++ /dev/null @@ -1,18 +0,0 @@ -(file-dispatch)= -# File 'dispatch' - -The `dispatch` file in your charm is an executable shell script whose responsibility is to execute the `src/charm.py` file with certain environment variables. - -The file is created automatically by `charmcraft pack` and you can inspect it by unzipping the `.charm` archive (`unzip .charm` ) or by deploying the charm, SSHing into one its units, and inspecting the charm directory in there (e.g., for unit `0`: `ls agents/unit--0/charm`). - ---- -```{dropdown} Expand to view contents of a sample dispatch file - -```bash -#!/bin/sh - -JUJU_DISPATCH_PATH="${JUJU_DISPATCH_PATH:-$0}" PYTHONPATH=lib:venv \ - exec ./src/charm.py -``` - -``` \ No newline at end of file diff --git a/docs/reference/files/file-dispatch.rst b/docs/reference/files/file-dispatch.rst new file mode 100644 index 000000000..cf2f37022 --- /dev/null +++ b/docs/reference/files/file-dispatch.rst @@ -0,0 +1,37 @@ +.. _file-dispatch: + +``dispatch`` +============ + + +The ``dispatch`` file in your charm the file `Juju`_ runs when an event occurs. + +In most cases, is an executable shell script whose responsibility is to execute +``src/charm.py`` file within your charm's virtual environment. + +The file is normally created automatically by ``charmcraft pack``, and you can +inspect it by extracting the ``.charm`` archive (``unzip .charm``). +The template for the default dispatch script is available `on Github +`_. + +Overriding ``dispatch`` +----------------------- + +While it is not recommended, it is possible to override the dispatch script with +the file of your choice, simply by placing a file called ``dispatch`` in the root +directory of the charm. The only requirements are that the file have the executable +bit set in its file mode and that it be executable on the platform where the charm +will be deployed. + +.. dropdown:: Example charm with an overridden ``dispatch`` script + + The following `part`_ section added to any charm will replace the dispatch + script with a script that does nothing (but successfully) upon any event. + + .. code:: + + parts: + dispatch: + plugin: nil + override-build: | + echo "!#/bin/true" >> "$CRAFT_PART_INSTALL"/dispatch From 1df7ac42851e43b3647c7c723adbea5b329d796a Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 12:22:13 -0500 Subject: [PATCH 15/19] docs: move icon.svg docs from markdown to rst --- docs/reference/files/file-dispatch.rst | 3 +- docs/reference/files/file-icon-svg.md | 24 ---------- docs/reference/files/file-icon-svg.rst | 61 ++++++++++++++++++++++++++ docs/reference/files/index.rst | 5 ++- 4 files changed, 65 insertions(+), 28 deletions(-) delete mode 100644 docs/reference/files/file-icon-svg.md create mode 100644 docs/reference/files/file-icon-svg.rst diff --git a/docs/reference/files/file-dispatch.rst b/docs/reference/files/file-dispatch.rst index cf2f37022..f4054e7b2 100644 --- a/docs/reference/files/file-dispatch.rst +++ b/docs/reference/files/file-dispatch.rst @@ -3,7 +3,6 @@ ``dispatch`` ============ - The ``dispatch`` file in your charm the file `Juju`_ runs when an event occurs. In most cases, is an executable shell script whose responsibility is to execute @@ -28,7 +27,7 @@ will be deployed. The following `part`_ section added to any charm will replace the dispatch script with a script that does nothing (but successfully) upon any event. - .. code:: + .. code:: yaml parts: dispatch: diff --git a/docs/reference/files/file-icon-svg.md b/docs/reference/files/file-icon-svg.md deleted file mode 100644 index 6d3cdc96f..000000000 --- a/docs/reference/files/file-icon-svg.md +++ /dev/null @@ -1,24 +0,0 @@ -(file-icon-svg)= -# File 'icon.svg' - -> {ref}`List of files in the charm project ` > `icon.svg` -> - -If you've uploaded your charm on Charmhub, once you've released the charm into a channel with the `stable` risk level, if your charm project includes an `icon.svg` file, the icon will be displayed in your charm's Charmhub profile. - -## Requirements - -- The file must have this exact name: `icon.svg`. -- The canvas size must be 100x100 pixels. -- The icon must consist of a circle with a flat color and a logo -- any other detail is up to you, but it's a good idea to also conform to best practices. - -## Best practices - -- Icons should have some padding between the edges of the circle and the logo. -- Icons should not be overly complicated. Charm icons are displayed in various sizes (from 160x160 to 32x32 pixels) and they should be always legible. - -```{tip} -In Inkscape, the ‘Icon preview’ tool can help you to check the sharpness of your icons at small sizes. -``` -- Symbols should have a similar weight on all icons: Avoid too thin strokes and use the whole space available to draw the symbol within the limits defined by the padding. However, if the symbol is much wider than it is high, it may overflow onto the horizontal padding area to ensure its weight is consistent. -- Do not use glossy materials unless they are parts of a logo that you are not allowed to modify. diff --git a/docs/reference/files/file-icon-svg.rst b/docs/reference/files/file-icon-svg.rst new file mode 100644 index 000000000..83e7ac5f4 --- /dev/null +++ b/docs/reference/files/file-icon-svg.rst @@ -0,0 +1,61 @@ +.. _file-icon-svg: + +``icon.svg`` +============ + +``icon.svg`` is the icon for your charm displayed on your charm's page on `Charmhub`_. + +Requirements +------------ + +- The file must have this exact name: ``icon.svg``. +- The canvas size must be 100x100 pixels. +- The icon must consist of a circle with a flat color and a logo -- any other detail + is up to you, but it's a good idea to also conform to best practices. + +Best practices +-------------- + +- Icons should have some padding between the edges of the circle and the logo. +- Icons should not be overly complicated. Charm icons are displayed in various sizes + (from 160x160 to 32x32 pixels) and they should be always legible. +- Symbols should have a similar weight on all icons: Avoid too thin strokes and use + the whole space available to draw the symbol within the limits defined by the + padding. However, if the symbol is much wider than it is high, it may overflow onto + the horizontal padding area to ensure its weight is consistent. +- Do not use glossy materials unless they are parts of a logo that you are not + allowed to modify. + +.. tip:: + + In `Inkscape `_, the ‘Icon preview’ tool can help + you check the sharpness of your icons at small sizes. + +.. dropdown:: Examples + + - `OpenSearch `_ + - `Traefik (Kubernetes) `_ + +Including the file +------------------ + +If your file is not in your project directory but not included in the charm, you can +use the :ref:`craft_parts_dump_plugin` to include this file in your charm. + +.. dropdown:: Example + + This example is the :ref:`file-charmcraft-yaml-parts` key of a + :ref:`file-charmcraft-yaml` that uses the :ref:`craft_parts_poetry_plugin` to + build a charm, augmented to add the charm's ``icon.svg`` file. + + .. code:: yaml + + parts: + my-charm: + plugin: poetry + source: . + icon: + plugin: dump + source: . + stage: + - icon.svg diff --git a/docs/reference/files/index.rst b/docs/reference/files/index.rst index 9bd6f9aef..7724f563a 100644 --- a/docs/reference/files/index.rst +++ b/docs/reference/files/index.rst @@ -1,7 +1,8 @@ +.. _list-of-files-in-a-charmcraft-project: .. _list-of-files-in-a-charm-or-a-bundle: -List of files in a charm or a bundle -==================================== +List of files in a Charmcraft project +===================================== .. toctree:: From b0965c16386d8750d6502080d7a01b103905f50a Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 13:27:37 -0500 Subject: [PATCH 16/19] docs: change .py to rst --- docs/reference/files/file-libname-py.md | 160 ----------- docs/reference/files/file-libname-py.rst | 324 +++++++++++++++++++++++ 2 files changed, 324 insertions(+), 160 deletions(-) delete mode 100644 docs/reference/files/file-libname-py.md create mode 100644 docs/reference/files/file-libname-py.rst diff --git a/docs/reference/files/file-libname-py.md b/docs/reference/files/file-libname-py.md deleted file mode 100644 index 050eeef0d..000000000 --- a/docs/reference/files/file-libname-py.md +++ /dev/null @@ -1,160 +0,0 @@ -(file-libname-py)= -# File `.py` - -File `.py` is a Python file (or a Python [module](https://realpython.com/lessons/scripts-modules-packages-and-libraries/)) in your charm project that holds a charm library -- that is, code that enables charm developers to easily share and reuse auxiliary logic related to charms -- for example, logic related to the relations between charms. - -Authors associate libraries with a specific charm, and publish them to Charmhub with a reference to the origin charm. This does not prevent reuse, modification, and sharing. - -The publishing tools around libraries are deliberately kept simple. Libraries are however versioned and uniquely identified. - - - - - -## Location - -Charm libraries are located in a directory inside the charm with the following structure: - - $CHARMDIR/lib/charms//v/.py - -where `$CHARMDIR` is the project's root (contains `src/`, `hooks/`, etc.), and the `` placeholder represents the charm responsible for the library named as `.py` with API version ``. - -For example, inside a charm `mysql`, the library `db` with major version 3 will be in a directory with the structure below: - - $CHARMDIR/lib/charms/mysql/v3/db.py - -When you pack your charm, Charmcraft copies the top `lib` directory into the root directory of the charm. Thus, to import this library in Python use the full path minus the top `lib` directory, as below: - -``` -import charms.mysql.v3.db -``` - - - -## Structure - -A charm library is a Python file with the following structure: - - -### Docstring - -Your charm file begins with a long docstring. This docstring describes your library. Charmcraft publishes it as your library's documentation on Charmhub. This documentation is updated whenever a new version of the library is published. - -The docstring supports Markdown, specifically, CommonMark. - - -### Metadata - -After the docstring, there are a few metadata keys, as below. - - -#### `LIBID` - -The `LIBID` key controls the unique identifier for a library across the entire universe of charms. The value is assigned by Charmhub to this particular library automatically at library creation time. This key enables Charmhub and `charmcraft` to track the library uniquely even if the charm or the library are renamed, which allows updates to warn and guide users through the process. - -#### `LIBAPI` - -`LIBAPI` is set to an initial state of `0`. In general, `LIBAPI` must match the major version in the import path. - -#### `LIBPATCH` - -`LIBPATCH` is set to an initial state of `1`. In general, it must match the current patch version (needs to be updated when changing). - -#### `PYDEPS` - -The `PYDEPS` key is an optional key that allows you to declare external Python dependencies. Charmcraft will make sure to add them to the dependencies required for your charm. - -The key maps to a list of strings. Each string is a regular "pip installable" Python dependency that will be retrieved from PyPI in the usual way (subject to the user's system configuration) and which supports all dependency formats (just the package name, a link to a Github project, etc.). - -Some examples of possible PYDEPS declarations are as below: - -``` -PYDEPS=["jinja2"] - -PYDEPS = ["pyyaml", "httpcore<0.15.0,>=0.14.5"] - -PYDEPS = [ - "git+https://github.com/canonical/operator/#egg=ops", - "httpcore<0.15.0,>=0.14.5", - "requests", -] -``` - -Of course, only one `PYDEPS` declaration is allowed. - -Charmcraft will collect the dependencies from all libraries included in the charm and install them from source in the virtual environment inside the `.charm` file, together with all the other Python dependencies declared by the charm itself. - -Note that, when called to install all the dependencies from the charm and all the used libraries, `pip` may detect conflicts between the requested packages and their versions. This is a feature, because it's always better to detect incompatibilities between dependencies at this moment than when the charm is being deployed or run after deployment. - - -### Code - -After the docstring and the metadata, there's the library code. This is regular Python code. - -## Popular library index - - -This is an index of the charm libraries that are currently known to be available. - -```{note} - -This list may be missing libraries from charms that are not publicly listed on Charmhub. If you would like to add a library to the list, please drop a comment using the feedback link below. - -``` - -### Libraries that define relations - -The following libraries provide programmatic instructions for relating to a specific charm. - -| Library | Used in | Description | -| --- | --- | --- | -| [fluentbit](https://charmhub.io/fluentbit/libraries/fluentbit) | [fluentbit](https://charmhub.io/fluentbit/libraries/fluentbit) | Defines both sides of a relation interface to the [fluentbit charm](https://charmhub.io/fluentbit/libraries/fluentbit). | -| [redis](https://charmhub.io/redis-k8s/libraries/redis) | | Import RedisRequires from this lib to relate your charm to the [redis-k8s charm](https://charmhub.io/redis-k8s). | -| [grafana-dashboard](https://charmhub.io/grafana-k8s/libraries/grafana-dashboard) | | Defines a relation interface for charms that provide a dashboard to the [grafana-k8s charm](https://charmhub.io/grafana-k8s). | -| [grafana-source](https://charmhub.io/grafana-k8s/libraries/grafana-source) | | Defines a relation interface for charms that serve as a data source for the [grafana-k8s charm](https://charmhub.io/grafana-k8s). | -| [prometheus-scrape](https://charmhub.io/prometheus-k8s/libraries/prometheus_scrape) | | Defines a relation interface for charms that want to expose metrics endpoints to the [prometheus charm](https://charmhub.io/prometheus-k8s). | -|[alertmanager-dispatch](https://charmhub.io/alertmanager-k8s/libraries/alertmanager_dispatch) | | Defines a relation to the [alertmanager-dispatch charm](https://charmhub.io/alertmanager-k8s). | -|[karma_dashboard](https://charmhub.io/karma-k8s/libraries/karma_dashboard) | [karma-k8s](https://charmhub.io/karma-k8s) | Defines an interface for charms wishing to consume or provide a karma-dashboard relation. | -| [loki_push_api](https://charmhub.io/loki-k8s/libraries/loki_push_api) | [loki_k8s](https://charmhub.io/loki-k8s) | Defines a relation interface for charms wishing to provide or consume the Loki Push API---e.g., a charm that wants to send logs to Loki. | -| [log_proxy](https://charmhub.io/loki-k8s/libraries/log_proxy) | [loki_k8s](https://charmhub.io/loki-k8s) | Defines a relation interface that allows a charm to act as a Log Proxy for Loki (via the Loki Push API). | -| [guacd](https://charmhub.io/apache-guacd/libraries/guacd) | [apache-guacd](https://charmhub.io/apache-guacd/) | Defines a relation for charms wishing to set up a native server side proxy for Apache Guacamole. | - -### Libraries that provide tools - -These libraries provide reusable tooling, typically to interact with cloud services, or to perform operations common to several charms. -| Library | Used in | Description | -| --- | --- | --- | -| [cert](https://charmhub.io/kubernetes-dashboard/libraries/cert) | [kubernetes-dashboard](https://charmhub.io/kubernetes-dashboard) | Generates a self signed certificate. | -| [capture_events](https://discourse.charmhub.io/t/harness-recipe-capture-events/6581) | [traefik-k8s](https://charmhub.io/traefik-k8s), [data-platform-libs](https://github.com/canonical/data-platform-libs/) | Helper for unittesting events. | -| [networking](https://discourse.charmhub.io/t/harness-and-network-mocks/6633) | | Provides tools for mocking networks. | -| [compound-status](https://charmhub.io/compound-status) | | Provides utilities to track multiple independent statuses in charms. | -| [resurrect](https://github.com/PietroPasotti/resurrect) | [github-runner-image-builder](https://github.com/canonical/github-runner-image-builder-operator) | Provides utilities to periodically trigger charm hooks. | - - -#### Libraries that provide tools for Kubernetes charms - -These libraries provide tooling for charms that run on top of Kubernetes clouds. - -| Library | Used in | Description | -| --- | --- | --- | -| [kubernetes_service_patch](https://charmhub.io/observability-libs/libraries/kubernetes_service_patch) | [cos-configuration-k8s](https://charmhub.io/cos-configuration-k8s), [alertmanager-k8s](https://charmhub.io/alertmanager-k8s), [grafana-agent-k8s](https://charmhub.io/grafana-agent-k8s), [prometheus-k8s](https://charmhub.io/prometheus-k8s), [loki-k8s](https://charmhub.io/loki-k8s), [traefik-k8s](https://charmhub.io/traefik-k8s) | Allows charm authors to simply and elegantly define service overrides that persist through a charm upgrade. | -| [ingress](https://charmhub.io/nginx-ingress-integrator/libraries/ingress) | [nginx-ingress-integrator](https://charmhub.io/nginx-ingress-integrator) | Configures nginx to use an existing Kubernetes Ingress. | -| [ingress-per-unit](https://charmhub.io/traefik-k8s/libraries/ingress_per_unit) | [traefik-k8s](https://charmhub.io/traefik-k8s) | Configures traefik to provide per-unit routing. | - -#### Libraries that provide tools for machine charms - -These libraries contain tools meant for use in machine charms, e.g., libraries that interact with package managers or other CLI tools that are often not present in containers. - -| Library | Used in | Description | -| --- | --- | --- | -| [apt](https://charmhub.io/operator-libs-linux/libraries/apt) | [mysql](https://charmhub.io/mysql), [zookeeper](https://charmhub.io/zookeeper), [cos-proxy](https://charmhub.io/cos-proxy), [kafka](https://charmhub.io/kafka), [ceph-mon](https://charmhub.io/ceph-mon) | Install and manage packages via `apt`. | -| [dnf](https://charmhub.io/operator-libs-linux/libraries/dnf) | | Install and manage packages via `dnf`. | -| [grub](https://charmhub.io/operator-libs-linux/libraries/grub) | | Manage kernel configuration via `grub`. | -| [passwd](https://charmhub.io/operator-libs-linux/libraries/passwd) | | Manage users and groups on a Linux system. | -| [snap](https://charmhub.io/operator-libs-linux/libraries/snap) | [mongodb](https://charmhub.io/mongodb), [mongodb-k8s](https://charmhub.io/mongodb-k8s), [postgresql](https://charmhub.io/postgresql), [grafana-agent-k8s](https://charmhub.io/grafana-agent-k8s), [kafka](https://charmhub.io/kafka) | Install and manage packages via `snapd`. | -| [sysctl](https://charmhub.io/operator-libs-linux/libraries/sysctl) | [kafka](https://charmhub.io/kafka) | Manage sysctl configuration. | -| [systemd](https://charmhub.io/operator-libs-linux/libraries/systemd) | [mongodb](https://charmhub.io/mongodb), [pgbouncer](https://charmhub.io/pgbouncer), [cos-proxy](https://charmhub.io/cos-proxy), [ceph-mon](https://charmhub.io/ceph-mon), [calico](https://charmhub.io/calico) | Interact with services via `systemd`. | - diff --git a/docs/reference/files/file-libname-py.rst b/docs/reference/files/file-libname-py.rst new file mode 100644 index 000000000..d207a9cc6 --- /dev/null +++ b/docs/reference/files/file-libname-py.rst @@ -0,0 +1,324 @@ +.. _file-libname-py: + +``.py`` +================ + +File ``.py`` is a Python file in your charm project that holds a charm +library -- that is, code that enables charm developers to easily share and reuse +auxiliary logic related to charms -- for example, logic related to the relations +between charms. + +Authors associate libraries with a specific charm and publish them to Charmhub with +a reference to the origin charm. This does not prevent reuse, modification or sharing. + +The publishing tools around libraries are deliberately kept simple. +Libraries are however versioned and uniquely identified. + + +Location +-------- + +Charm libraries are located in a subdirectory inside the charm with the following +structure: + +.. code:: text + + lib/charms//v/.py + +where the ```` placeholder represents the name of charm responsible for +the library (converted to a valid module name), ```` represents this +particular library, and ```` represents the API version of the library. + +For example, inside a charm ``mysql``, the library ``db`` with major version 3 will +be in a directory with the structure below: + +.. code:: text + + lib/charms/mysql/v3/db.py + +When you pack your charm, Charmcraft copies the top ``lib`` directory into the root +directory of the charm. Thus, to import this library in Python use the full path +minus the top ``lib`` directory, as below: + +.. code:: text + + import charms.mysql.v3.db + +Structure +--------- + +A charm library is a Python file with the following structure: + + +Docstring +~~~~~~~~~ + +Your charm file begins with a long docstring. This docstring describes your library. +Charmcraft publishes it as your library's documentation on Charmhub. This +documentation is updated whenever a new version of the library is published. + +The docstring is expected to be in the `CommonMark `_ +dialect of Markdown. + +Metadata +~~~~~~~~ + +After the docstring, there are a few metadata keys, as below. + +``LIBID`` +^^^^^^^^^ + +**Status:** Required + +**Purpose:** Contains the unique identifier for a library across the entire +universe of charms. The value is assigned by Charmhub to this particular library +automatically at library creation time. This key enables Charmhub and ``charmcraft`` +to track the library uniquely even if the charm or the library are renamed, which +allows updates to warn and guide users through the process. + +**Type:** ``str`` + +**Value:** Assigned by :ref:`ref_commands_create-lib` + +``LIBAPI`` +^^^^^^^^^^ + +**Status:** Required + +**Purpose:** Declares the API version of this charm library. + +**Type:** ``int`` + +**Value:** ``LIBAPI``` is set to an initial state of ``0``. In general, +``LIBAPI`` must match the major version in the import path. + +``LIBPATCH`` +^^^^^^^^^^^^ + +**Status:** Required + +**Purpose:** Declares the patch version of this charm library. + +**Type:** ``int`` + +**Value:** ``LIBPATCH`` is set to an initial state of ``1``. In general, it must +match the current patch version (needs to be updated when changing). + +.. note:: + + While ``LIBPATCH`` can be set to ``0``, it is not allowed to set both ``LIBAPI`` + and ``LIBPATCH`` to ``0``. As such, a charm lib may have a version ``0.1`` and + a version ``1.0``, but not a version ``0.0``. + +``PYDEPS`` +^^^^^^^^^^ + +**Status:** Optional + +**Purpose:** Declares external Python dependencies for the library. + +When using the ``charm`` plugin, Charmcraft will make sure to install them in the +virtual environment created in any charm that includes the library. + +**Type:** ``list[str]`` + +Each string is a regular "pip installable" Python dependency that will be retrieved +from PyPI in the usual way (subject to the user's system configuration) and which +supports all dependency formats (just the package name, a link to a Github project, etc.). + +.. dropdown:: Examples + + .. code:: python + + PYDEPS=["jinja2"] + + .. code:: python + + PYDEPS = ["pyyaml", "httpcore<0.15.0,>=0.14.5"] + + .. code:: python + + PYDEPS = [ + "git+https://github.com/canonical/operator/#egg=ops", + "httpcore<0.15.0,>=0.14.5", + "requests", + ] + +Note that when called to install all the dependencies from the charm and all the used +libraries, `pip` may detect conflicts between the requested packages and their versions. +This is a feature, because it's always better to detect incompatibilities between +dependencies at this moment than when the charm is being deployed or run after +deployment. + +Code +^^^^ + +After the docstring and the metadata, there's the library code. This is regular Python code. + +Popular libraries +----------------- + +This is a list of some popular charm libraries available from Charmhub. + +.. note:: + + This list does not and will not contain all charm libraries on Charmhub. However if + you believe a library is missing from this list, please + `open a pull request `_ adding + the library you believe to be missing. + +Libraries that define relations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following libraries provide programmatic instructions for relating to a specific +charm. + +.. list-table:: + :header-rows: 1 + + * - Library + - Used in + - Description + * - `fluentbit `_ + - - `fluentbit `_ + - Defines both sides of a relation interface to the + `fluentbit charm `_ + * - `redis `_ + - + - Import RedisRequires from this lib to relate your charm to the + `redis charm `_ + * - `grafana_dashboard `_ + - + - Defines a relation interface for charms that provide a dashboard to the + `grafana-k8s charm `_ + * - `grafana_source `_ + - + - Defines a relation interface for charms that serve as a data source for the + `grafana-k8s charm `_ + * - `prometheus_scrape `_ + - + - Defines a relation interface for charms that want to expose metrics endpoints to the + `prometheus charm `_. + * - `alertmanager_dispatch + `_ + - + - Defines a relation to the `alertmanager-dispatch charm + `_. + * - `karma_dashboard `_ + - - `karma-k8s `_ + - Defines an interface for charms wishing to consume or provide a karma-dashboard relation. + * - `loki_push_api `_ + - - `loki-k8s `_ + - Defines a relation interface for charms wishing to provide or consume the + Loki Push API---e.g., a charm that wants to send logs to Loki. + * - `log_proxy `_ + - - `loki-k8s `_ + - Defines a relation interface that allows a charm to act as a Log Proxy for + Loki (via the Loki Push API). + * - `guacd `_ + - - `apache-guacd `_ + - Defines a relation for charms wishing to set up a native server side proxy + for Apache Guacamole. + +Libraries that provide tools +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These libraries provide reusable tooling, typically to interact with cloud services, +or to perform operations common to several charms. + +.. list-table:: + :header-rows: 1 + + * - Library + - Used in + - Description + * - `cert `_ + - - `kubernetes-dashboard `_ + - Generates a self signed certificate. + * - `capture_events `_ + - - `traefik-k8s `_, + - `data-platform-libs `_ + - Helper for unit testing events. + * - `networking `_ + - + - Provides tools for mocking networks. + * - `compound-status `_ + - + - Provides utilities to track multiple independent statuses in charms. + * - `resurrect `_ + - - `github-runner-image-builder `_ + - Provides utilities to periodically trigger charm hooks + + +Libraries that provide tools for Kubernetes charms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These libraries provide tooling for charms that run on top of Kubernetes clouds. + +.. list-table:: + :header-rows: 1 + + * - Library + - Used in + - Description + * - `kubernetes_service_patch `_ + - - `cos-configuration-k8s `_ + - `alertmanager-k8s `_ + - `grafana-agent-k8s `_ + - `prometheus-k8s `_ + - `loki-k8s `_ + - `traefik-k8s `_ + - Allows charm authors to simply and elegantly define service overrides that persist through a charm upgrade. + * - `ingress `_ + - - `nginx-ingress-integrator `_ + - Configures nginx to use an existing Kubernetes Ingress. + * - `ingress-per-unit `_ + - - `traefik-k8s `_ + - Configures traefik to provide per-unit routing. + +Libraries that provide tools for machine charms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These libraries contain tools meant for use in machine charms, e.g., libraries that +interact with package managers or other CLI tools that are often not present in +containers. + +.. list-table:: + :header-rows: 1 + + * - Library + - Used in + - Description + * - `apt `_ + - - `mysql `_ + - `zookeeper `_ + - `cos-proxy `_ + - `kafka `_ + - `ceph-mon `_ + - Install and manage packages via ``apt``. + * - `dnf `_ + - + - Install and manage packages via ``dnf``. + * - `grub `_ + - + - Manage kernel configuration via ``grub``. + * - `passwd `_ + - + - Manage users and groups on a Linux system. + * - `snap `_ + - - `mongodb `_ + - `mongodb-k8s `_ + - `postgresql `_ + - `grafana-agent-k8s `_ + - `kafka `_ + - Install and manage packages via ``snapd``. + * - `sysctl `_ + - - `kafka `_ + - Manage sysctl configuration. + * - `systemd `_ + - - `mongodb `_ + - `pgbouncer `_ + - `cos-proxy `_ + - `ceph-mon `_ + - `calico `_ + - Interact with services via ``systemd``. From 9d2d880e2ac060796f0c577eb8a09f80b3db1aac Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 16:36:57 -0500 Subject: [PATCH 17/19] docs: fix linting issues --- .editorconfig | 5 +- docs/conf.py | 1 + docs/howto/index.rst | 1 + docs/howto/manage-bundles.rst | 12 +- docs/howto/manage-channels.rst | 5 +- docs/howto/manage-charms.rst | 290 +++++++++++------- docs/howto/manage-extensions.rst | 68 ++-- docs/howto/manage-icons.rst | 11 +- docs/howto/manage-libraries.rst | 72 +++-- docs/howto/manage-names.rst | 5 +- docs/howto/manage-parts.rst | 1 + docs/howto/manage-resources.rst | 41 ++- docs/howto/manage-revisions.rst | 28 +- docs/howto/manage-the-charmcraft-cli.rst | 20 +- .../manage-the-current-charmhub-user.rst | 7 +- docs/howto/manage-tracks.rst | 63 ++-- docs/howto/misc/index.rst | 5 +- ...ck-a-hooks-based-charm-with-charmcraft.rst | 18 +- ...pack-a-reactive-charm-with-charmcraft.rst} | 12 +- docs/reference/charmcraft-cli.rst | 5 +- docs/reference/files/file-bundle-yaml.rst | 69 +++-- docs/reference/files/file-charmcraft-yaml.rst | 211 ++++++++++--- docs/reference/files/file-config-yaml.rst | 19 +- docs/reference/files/file-dispatch.rst | 2 +- docs/reference/files/file-libname-py.rst | 48 +-- docs/reference/part.rst | 2 +- docs/reuse/links.txt | 1 + docs/tutorial/flask.rst | 4 +- docs/tutorial/index.rst | 6 +- 29 files changed, 693 insertions(+), 339 deletions(-) rename docs/howto/misc/{pack-a-reactive-based-charm-with-charmcraft.rst => pack-a-reactive-charm-with-charmcraft.rst} (67%) diff --git a/.editorconfig b/.editorconfig index dabc6ed8d..cafe667ef 100644 --- a/.editorconfig +++ b/.editorconfig @@ -27,10 +27,13 @@ tab_width = 2 indent_size = 2 max_line_length = off -[{*.markdown,*.md,*.rst}] +[{*.markdown,*.md}] max_line_length = off ij_visual_guides = none +[{*.rst}] +max_line_length = off + [{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] max_line_length = off diff --git a/docs/conf.py b/docs/conf.py index 3349ec8e0..36d8f06e0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -129,6 +129,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "craft-parts": ("https://canonical-craft-parts.readthedocs-hosted.com/en/latest/", None), + "ops": ("https://ops.readthedocs.io/en/latest/", None), "rockcraft": ("https://documentation.ubuntu.com/rockcraft/en/stable/", None), } # See also: diff --git a/docs/howto/index.rst b/docs/howto/index.rst index a3cd99c29..c77ebd23b 100644 --- a/docs/howto/index.rst +++ b/docs/howto/index.rst @@ -1,4 +1,5 @@ .. _how-to-guides: + How-to guides ============= diff --git a/docs/howto/manage-bundles.rst b/docs/howto/manage-bundles.rst index c37f7b712..fabc83b52 100644 --- a/docs/howto/manage-bundles.rst +++ b/docs/howto/manage-bundles.rst @@ -1,4 +1,5 @@ -.. manage-charm-bundles: +.. _manage-charm-bundles: + How to manage charm bundles =========================== @@ -16,8 +17,13 @@ To create a bundle, create a ``.yaml`` file with your desired configurat See more: :ref:`file-bundle-yaml` .. tip:: - If you don't want to start from scratch, export the contents of your model to a ``.yaml`` file via ``juju export-bundle --filename .yaml`` or download the ``.yaml`` of an existing bundle from Charmhub. See more: `Juju \| How to compare and export the contents of a model to a bundle `_. - + If you don't want to start from scratch, export the contents of your model to a + ``.yaml`` file via ``juju export-bundle --filename .yaml`` or + download the ``.yaml`` of an existing bundle from Charmhub. + See more: `Juju \| How to compare and export the contents of a model to a bundle + `_. + Pack a bundle ------------- diff --git a/docs/howto/manage-channels.rst b/docs/howto/manage-channels.rst index 30229a0b4..8923a4606 100644 --- a/docs/howto/manage-channels.rst +++ b/docs/howto/manage-channels.rst @@ -1,11 +1,14 @@ .. _manage-channels: + How to manage channels ====================== Create a channel ---------------- -When you register a name on Charmhub, that automatically creates 4 channels, all with track ``latest`` but with different risk levels, namely, ``edge``, ``beta``, ``candidate``, ``stable``, respectively. +When you register a name on Charmhub, that automatically creates 4 channels, all +with track ``latest`` but with different risk levels, namely, ``edge``, ``beta``, +``candidate``, ``stable``, respectively. See more: :ref:`register-a-name` diff --git a/docs/howto/manage-charms.rst b/docs/howto/manage-charms.rst index 8d7c91c3b..845a169e7 100644 --- a/docs/howto/manage-charms.rst +++ b/docs/howto/manage-charms.rst @@ -1,8 +1,10 @@ .. _manage-charms: + How to manage charms ==================== - See first: `Juju \| Charm `_, `Juju \| Manage charms `_ + See first: `Juju \| Charm `_, + `Juju \| Manage charms `_ Initialise a charm ------------------ @@ -21,37 +23,34 @@ content. .. dropdown:: Example session .. code:: text - + $ mkdir my-flask-app-k8s $ cd my-flask-app-k8s/ $ charmcraft init --profile flask-framework - Charmed operator package file and directory tree initialised. - - Now edit the following package files to provide fundamental charm metadata - and other information: - - charmcraft.yaml - src/charm.py - README.md - + Charmed operator package file and directory tree initialised + Now edit the following package files to provide fundamental charm metadata + and other information: + + charmcraft.yaml + src/charm.py + README.md + $ ls -R .: charmcraft.yaml requirements.txt src - + ./src: charm.py - - - :: - - +The command also allows you to not specify any profile (in that case you get the +``simple`` profile -- a Kubernetes profile with lots of scaffolding, suitable for +beginners) and has flags that you can use to specify a different directory to +operate in, a charm name different from the name of the root directory, etc. -The command also allows you to not specify any profile (in that case you get the `simple` profile -- a Kubernetes profile with lots of scaffolding, suitable for beginners) and has flags that you can use to specify a different directory to operate in, a charm name different from the name of the root directory, etc. + See more: :ref:`ref_commands_revisions`, :ref:`profile`, + :ref:`list-of-files-in-a-charmcraft-project` - See more: :ref:`ref_commands_revisions`, :ref:`profile`, :ref:`list-of-files-in-a-charm-project` - - See more: :ref:`manage-extensions` + See more: :ref:`manage-extensions` Add charm project metadata, an icon, docs ----------------------------------------- @@ -60,33 +59,36 @@ Add charm project metadata, an icon, docs Specify that the project is a charm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To specify that the project is a charm (as supposed to a bundle), in your `charmcraft.yaml` file set the `type` key to `charm`: +To specify that the project is a charm (as supposed to a bundle), in your +``charmcraft.yaml`` file set the ``type`` key to ``charm``: .. code:: yaml - + type: charm Specify a name ~~~~~~~~~~~~~~ -To specify a pack-and-deploy name for your charm, in your charm's `charmcraft.yaml` file specify the `name` key. E.g., +To specify a pack-and-deploy name for your charm, in your charm's +``charmcraft.yaml`` file specify the ``name`` key. E.g., .. code:: yaml - + name: traefik-k8s -.. +.. See more: :ref:`file-charmcraft-yaml-name` Specify a title ~~~~~~~~~~~~~~~ -To specify a title for your charm's page on Charmhub, in your charm's `charmcraft.yaml` file specify a value for the `title` key. E.g., +To specify a title for your charm's page on Charmhub, in your charm's +``charmcraft.yaml`` file specify a value for the ``title`` key. E.g., .. code:: yaml - + title: | Traefik Ingress Operator for Kubernetes @@ -97,10 +99,11 @@ To specify a title for your charm's page on Charmhub, in your charm's `charmcraf Add a summary ~~~~~~~~~~~~~ -To add a summary line for your charm, in your charm's `charmcraft.yaml` file specify a value for the `summary` key. E.g., +To add a summary line for your charm, in your charm's ``charmcraft.yaml`` file +specify a value for the ``summary`` key. E.g., .. code:: yaml - + summary: | A Juju charm to run a Traefik-powered ingress controller on Kubernetes. @@ -111,14 +114,15 @@ To add a summary line for your charm, in your charm's `charmcraft.yaml` file spe Add a description ~~~~~~~~~~~~~~~~~ -To add a longer description for your charm, in your charm's `charmcraft.yaml` file specify a value for the `description` key. E.g., +To add a longer description for your charm, in your charm's ``charmcraft.yaml`` +file specify a value for the ``description`` key. E.g., .. code:: yaml - + description: | A Juju-operated Traefik operator that routes requests from the outside of a Kubernetes cluster to Juju units and applications. - + .. @@ -127,10 +131,12 @@ To add a longer description for your charm, in your charm's `charmcraft.yaml` fi Add contact information ~~~~~~~~~~~~~~~~~~~~~~~ -To add maintainer contact information for a charm, in your charm's `charmcraft.yaml` file specify a value for the `links.contact` key. E.g., +To add maintainer contact information for a charm, in your charm's ``charmcraft.yaml`` +file specify a value for the :ref:`links.contact ` +key. E.g., .. code:: yaml - + links: contact: Please send your answer to Old Pink, care of the Funny Farm, Chalfont @@ -142,55 +148,67 @@ To add maintainer contact information for a charm, in your charm's `charmcraft.y Add a link to source code ~~~~~~~~~~~~~~~~~~~~~~~~~ -To add a link to the source code for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.source` key. E.g., +To add a link to the source code for a charm, in your charm's ``charmcraft.yaml`` +file specify an item under the :ref:`links.source ` +key. E.g., .. code:: yaml - + links: source: - https://github.com/canonical/traefik-k8s-operator .. - + See more: :ref:`file-charmcraft-yaml-links` Add a link to the bug tracker ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To add a link to the bug tracker for a charm, in your charm's `charmcraft.yaml` file specify an item under the `links.issues` key. E.g., +To add a link to the bug tracker for a charm, in your charm's ``charmcraft.yaml`` +file specify an item under the :ref:`links.issues ` +key. E.g., .. code:: yaml - + links: - issues: + issues: - https://github.com/canonical/traefik-k8s-operator/issues .. - + See more: :ref:`file-charmcraft-yaml-links` Add a link to the website ~~~~~~~~~~~~~~~~~~~~~~~~~ -If your charm has a website outside of Charmhub, to add a link to this website, in your charm's `charmcraft.yaml` file specify an item under the `links.website` key. E.g., +If your charm has a website outside of Charmhub, to add a link to this website, in +your charm's ``charmcraft.yaml`` file specify an item under the +:ref:`links.website ` key. E.g., .. code:: yaml - + links: website: - https://charmed-kubeflow.io/ .. - + See more: :ref:`file-charmcraft-yaml-links` Add docs and a link to the docs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you publish your charm on Charmhub, reference documentation about the charm's resources, actions, configurations, relations, and libraries is extracted automatically. However, you should also aim to add further docs, e.g., a tutorial, how-to guides, etc. To provide a link to these docs, in your charm's `charmcraft.yaml` file specify a value for the `links.documentation` key. Note that at present this must be a Discourse page. E.g., +If you publish your charm on Charmhub, reference documentation about the charm's +resources, actions, configurations, relations, and libraries is extracted +automatically. However, you should also aim to add further docs, e.g., a tutorial, +how-to guides, etc. To provide a link to these docs, in your charm's +``charmcraft.yaml`` file specify a value for the +:ref:`links.documentation ` key. +Note that at present this must be a Discourse page. E.g., .. code:: yaml - + links: documentation: https://discourse.charmhub.io/t/traefik-k8s-docs-index/10778 @@ -202,17 +220,18 @@ If you publish your charm on Charmhub, reference documentation about the charm's Add terms of use ~~~~~~~~~~~~~~~~ - To add terms of use for your charm, in your charm's `charmcraft.yaml` file specify a value for the `terms` key. E.g., + To add terms of use for your charm, in your charm's ``charmcraft.yaml`` + file specify a value for the ``terms`` key. E.g., .. code:: yaml - + terms: - Butterscotch is regal - Cara is adorable .. - + See more: :ref:`file-charmcraft-yaml-terms` @@ -228,48 +247,51 @@ Add runtime details to a charm Require a specific Juju version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To require a specific Juju version for your charm, in your charm's `charmcraft.yaml` specify the `assumes` key. E.g., +To require a specific Juju version for your charm, in your charm's +``charmcraft.yaml`` specify the ``assumes`` key. E.g., .. code:: yaml - + assumes: - juju >= 3.5 .. - + See more: :ref:`file-charmcraft-yaml-assumes` Require a Kubernetes cloud ~~~~~~~~~~~~~~~~~~~~~~~~~~ -To require a Kubernetes cloud for your charm, in your charm's `charmcraft.yaml` file specify the `assumes` key. E.g., +To require a Kubernetes cloud for your charm, in your charm's +``charmcraft.yaml`` file specify the ``assumes`` key. E.g., .. code:: yaml - + assumes: - k8s-api .. - + See more: :ref:`file-charmcraft-yaml-assumes` Require a specific base and platforms ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To require a specific base and platforms for your charm, in your charm's `charmcraft.yaml` file specify the `base`(,`build-base`,) and the `platforms keys. E.g., +To require a specific base and platforms for your charm, in your charm's +``charmcraft.yaml`` file specify the ``base``, and ``platforms`` keys. E.g., .. note:: - In Charmcraft < 3.0 this was done via a single key: `bases`. + In Charmcraft < 3.0 this was done via a single key: ``bases``. .. code:: yaml - + # The run time base, the base format is @, # accepted bases are: # - ubuntu@24.04 base: - # The build time base, if not defined the base is also the build time + # The build time base, if not defined the base is also the build time # base, in addition to valid bases, the build-base can be "devel" # which would use the latest in development Ubuntu Series. build-base: @@ -292,12 +314,13 @@ To require a specific base and platforms for your charm, in your charm's `charmc .. - See more: :ref:`file-charmcraft-yaml-base`, :ref:`build-base`, :ref:`file-charmcraft-yaml-platforms` + See more: :ref:`file-charmcraft-yaml-base`, :ref:`file-charmcraft-yaml-build-base` Specify container requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To specify container requirements, in your charm's `charmcraft.yaml` file specify the `containers` key. +To specify container requirements, in your charm's ``charmcraft.yaml`` file +specify the ``containers`` key. See more: :ref:`file-charmcraft-yaml-containers` @@ -306,35 +329,41 @@ To specify container requirements, in your charm's `charmcraft.yaml` file specif Specify associated resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To specify the resources associated with the charm, in your charm's `charmcraft.yaml` file specify the `resources` key. +To specify the resources associated with the charm, in your charm's +``charmcraft.yaml`` file specify the ``resources`` key. See :ref:`manage-resources`. Specify device requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To specify device requirements, in your charm's `charmcraft.yaml` file specify the `devices` key. +To specify device requirements, in your charm's ``charmcraft.yaml`` file specify +the ``devices`` key. See more: :ref:`file-charmcraft-yaml-devices` Specify storage requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To specify storage requirements, in your charm's `charmcraft.yaml` file specify the `storage` key. +To specify storage requirements, in your charm's ``charmcraft.yaml`` file specify +the ``storage`` key. See more: :ref:`file-charmcraft-yaml-storage` Specify extra binding requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To specify extra binding requirements, in your charm's `charmcraft.yaml` file specify the `extra-bindings` key. +To specify extra binding requirements, in your charm's ``charmcraft.yaml`` file +specify the ``extra-bindings`` key. See more: :ref:`file-charmcraft-yaml-extra-bindings` Require subordinate deployment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To require subordinate deployment for your charm (i.e., for it to be deployed to the same machine as another charm, called its 'principal'), in your charm's `charmcraft.yaml` file specify the `subordinate` key. +To require subordinate deployment for your charm (i.e., for it to be deployed +to the same machine as another charm, called its 'principal'), in your charm's +``charmcraft.yaml`` file specify the ``subordinate`` key. See more: :ref:`file-charmcraft-yaml-subordinate` @@ -342,40 +371,51 @@ To require subordinate deployment for your charm (i.e., for it to be deployed to Manage actions ~~~~~~~~~~~~~~ - See first: `Juju \| Action `_, `Juju \| Manage actions `_ + See first: `Juju \| Action `_, + `Juju \| Manage actions `_ -To declare an action in your charm, in your charm's `charmcraft.yaml` file specify the `actions` key. +To declare an action in your charm, in your charm's ``charmcraft.yaml`` file +specify the ``actions`` key. See more: :ref:`file-charmcraft-yaml-actions` - See next: `Ops \| Manage actions `_ + See next: `Ops \| Manage actions + `_ Manage configurations ~~~~~~~~~~~~~~~~~~~~~ - See first: `Juju \| Application configuration `_, `Juju \| Manage applications > Configure `_ + See first: `Juju \| Application configuration + `_, + `Juju \| Manage applications > Configure + `_ -To declare a configuration option for your charm, in your charm's `charmcraft.yaml` specify the `config` key. +To declare a configuration option for your charm, in your charm's ``charmcraft.yaml`` +specify the ``config`` key. See more: :ref:`file-charmcraft-yaml-config` - See next: `Ops \| Manage configurations `_ + See next: `Ops \| Manage configurations + `_ Manage relations (integrations) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - See first: `Juju \| Relation `_, `Juju \| Manage relations `_ + See first: `Juju \| Relation `_, + `Juju \| Manage relations `_ - To declare a relation endpoint in your charm, in your charm's `charmcraft.yaml` specify the `peers`, `provides`, or `requires` key. + To declare a relation endpoint in your charm, in your charm's ``charmcraft.yaml`` + specify the ``peers``, ``provides``, or ``requires`` key. - See more: :ref:`file-charmcraft-yaml-peers-provides-requires` + See more: :ref:`file-charmcraft-yaml-peers` - See more: `Ops \| Manage relations (integrations) `_ + See more: `Ops \| Manage relations (integrations) + `_ Specify necessary libs ~~~~~~~~~~~~~~~~~~~~~~ @@ -390,11 +430,14 @@ Manage secrets See first: `Juju \| User secret `_ -To make your charm capable of accepting a user secret, in your charm's `charmcraft.yaml` specify the `config` key with the `type` subkey set to `secret`. +To make your charm capable of accepting a user secret, in your charm's +``charmcraft.yaml`` specify the ``config`` key with the ``type`` subkey set to +``secret``. See more: :ref:`file-charmcraft-yaml-config` - - See next: `Ops \| Manage secrets `_ + + See next: `Ops \| Manage secrets + `_ Specify necessary parts ~~~~~~~~~~~~~~~~~~~~~~~ @@ -407,29 +450,32 @@ Pack a charm To pack a charm directory, in the charm's root directory, run the command below: .. code:: text - + charmcraft pack -This will fetch any dependencies (from PyPI, based on `requirements.txt`), compile any modules, check that all the key files are in place, and produce a compressed archive with the extension `.charm`. As you can verify, this archive is just a zip file with metadata and the operator code itself. +This will fetch any dependencies (from PyPI, based on ``requirements.txt``), +compile any modules, check that all the key files are in place, and produce a +compressed archive with the extension ``.charm``. As you can verify, this archive +is just a zip file with metadata and the operator code itself. .. dropdown:: Example session for a charm called microsample-vm .. code:: text - + # Pack the charm: ~/microsample-vm$ charmcraft pack - Created 'microsample-vm_ubuntu-22.04-amd64.charm'. - Charms packed: - microsample-vm_ubuntu-22.04-amd64.charm - + Created 'microsample-vm_ubuntu-22.04-amd64.charm'. + Charms packed: + microsample-vm_ubuntu-22.04-amd64.charm + # (Optional) Verify that this has created a .charm file in your charm's root directory: ~/microsample-vm$ ls CONTRIBUTING.md charmcraft.yaml requirements.txt tox.ini LICENSE microsample-vm_ubuntu-22.04-amd64.charm src README.md pyproject.toml tests - - # (Optional) Verify that the .charm file is simply a zip file that contains + + # (Optional) Verify that the .charm file is simply a zip file that contains # everything you've packed plus any dependencies: /microsample-vm$ unzip -l microsample-vm_ubuntu-22.04-amd64.charm | { head; tail;} Archive: microsample-vm_ubuntu-22.04-amd64.charm @@ -453,23 +499,30 @@ This will fetch any dependencies (from PyPI, based on `requirements.txt`), compi --------- ------- 20274163 1538 files -The command has a number of flags that allow you to specify a different charm directory to pack, whether to force pack if there are linting errors, etc. +The command has a number of flags that allow you to specify a different charm +directory to pack, whether to force pack if there are linting errors, etc. See more: :ref:`ref_commands_pack` .. caution:: - **If you’ve declared any resources :** This will *not* pack the resources. This means that, when you upload your charm to Charmhub (if you do), you will have to upload the resources separately. See more: :ref:`manage-resources`. + **If you’ve declared any resources :** This will *not* pack the resources. + This means that, when you upload your charm to Charmhub (if you do), you will + have to upload the resources separately. See more: :ref:`manage-resources`. .. important:: - When the charm is packed, a series of analyses and lintings will happen, you may receive warnings and even errors to help improve the quality of the charm. See more: :ref:`Charmcraft analyzers and linters ` + When the charm is packed, a series of analyses and lintings will happen, + you may receive warnings and even errors to help improve the quality of the + charm. See more: + :ref:`Charmcraft analyzers and linters ` .. See next: `Juju \| Manage charms `_ .. _publish-a-charm: + Publish a charm on Charmhub --------------------------- @@ -493,10 +546,13 @@ Publish a charm on Charmhub See more: :ref:`manage-names` -.. note:; - This automatically creates 4 channels, all with track latest but with different risk levels, namely, edge, beta, candidate, stable, respectively. See more: :ref:`manage-channels`. +.. note:: + This automatically creates 4 channels, all with track ``latest`` but with different + risk levels, namely, edge, beta, candidate, stable, respectively. + See more: :ref:`manage-channels`. -3. Upload the charm to Charmhub: Use the ``charmcraft upload`` command followed by the your charm’s filepath. E.g., if you are in the charm’s root directory, +3. Upload the charm to Charmhub: Use the ``charmcraft upload`` command followed + by the your charm’s filepath. E.g., if you are in the charm’s root directory, .. code:: text @@ -508,7 +564,8 @@ Publish a charm on Charmhub See more: :ref:`ref_commands_upload` .. note:: - Each time you upload a charm to Charmhub, that creates a revision (unless you upload the exact same file again). See more: :ref:`manage-charm-revisions`. + Each time you upload a charm to Charmhub, that creates a revision (unless + you upload the exact same file again). See more: :ref:`manage-charm-revisions`. 4. If your charm has associated resources: These are not packed with the rest of the charm project, so you must upload them explicitly to @@ -524,7 +581,8 @@ Publish a charm on Charmhub See more: :ref:`manage-resources` .. note:: - Each time you upload a resource to Charmhub, that creates a revision (unless you upload the exact same file again). See more: :ref:`manage-resource-revisions`. + Each time you upload a resource to Charmhub, that creates a revision (unless + you upload the exact same file again). See more: :ref:`manage-resource-revisions`. 5. Release the charm: To release a charm, release your revision of choice to the target release channel. E.g., @@ -542,24 +600,34 @@ Publish a charm on Charmhub This automatically opens the channel. See more: :ref:`manage-channels`. .. - - See next: `Juju \| Deploy a Charmub charm `_, `Juju \| Update a Charmhub charm `_ + + See next: + `Juju \| Deploy a Charmub charm + `_, + `Juju \| Update a Charmhub charm + `_ .. tip:: To update the charm on Charmhub, repeat the upload and release steps. .. important:: - Releasing a charm on Charmhub gives it a public URL. However, the charm will not appear in the Charmhub search results until it has passed formal review. To request formal review, reach out to the community to announce your charm and ask for a review by an experienced community member. See more: `Discourse \| review requests `_. - - Also, the point of publishing and having a charm publicly listed on Charmhub is so others can reuse it and potentially contribute to it as well. To publicize your charm: - - - `Write a Discourse post to announce your - release. `_ - - - `Schedule a community workshop to demo your charm’s - capabilities. `_ - - - `Chat about it with your charmer - friends. `_ + Releasing a charm on Charmhub gives it a public URL. However, the charm will + not appear in the Charmhub search results until it has passed formal review. + To request formal review, reach out to the community to announce your charm + and ask for a review by an experienced community member. See more: `Discourse \| + review requests `_. + + Also, the point of publishing and having a charm publicly listed on Charmhub + is so others can reuse it and potentially contribute to it as well. To publicise + your charm: + + - `Write a Discourse post to announce your release. + `_ + + - `Schedule a community workshop to demo your charm’s capabilities. + `_ + + - `Chat about it with your charmer friends. + `_ .. diff --git a/docs/howto/manage-extensions.rst b/docs/howto/manage-extensions.rst index 2b13428e0..66057eedd 100644 --- a/docs/howto/manage-extensions.rst +++ b/docs/howto/manage-extensions.rst @@ -1,4 +1,5 @@ .. _manage-extensions: + How to manage extensions ======================== @@ -7,14 +8,15 @@ How to manage extensions View all the available extensions --------------------------------- -To view all the available Rockcraft / Charmcraft extensions, run the ``rockcraft list-extensions`` / ``charmcraft list-extensions`` command. For example: +To view all the available Rockcraft / Charmcraft extensions, run the +``rockcraft list-extensions`` / ``charmcraft list-extensions`` command. For example: .. code:: text $ charmcraft list-extensions Extension name Supported bases Experimental bases ---------------- ----------------- -------------------- - flask-framework ubuntu@22.04 + flask-framework ubuntu@22.04 .. @@ -31,34 +33,34 @@ extension. .. dropdown:: Example .. code:: text - + $ mkdir my-flask-app-k8s $ cd my-flask-app-k8s/ $ charmcraft init --profile flask-framework - Charmed operator package file and directory tree initialised. - - Now edit the following package files to provide fundamental charm metadata - and other information: - - charmcraft.yaml - src/charm.py - README.md - + Charmed operator package file and directory tree initialised. + + Now edit the following package files to provide fundamental charm metadata + and other information: + + charmcraft.yaml + src/charm.py + README.md + user@ubuntu:~/my-flask-app-k8s$ ls -R .: charmcraft.yaml requirements.txt src - + ./src: charm.py - - $ cat charmcraft.yaml + + $ cat charmcraft.yaml # This file configures Charmcraft. # See https://juju.is/docs/sdk/charmcraft-config for guidance. - + name: my-flask-app-k8s - + type: charm - + bases: - build-on: - name: ubuntu @@ -66,17 +68,17 @@ extension. run-on: - name: ubuntu channel: "22.04" - + # (Required) summary: A very short one-line summary of the flask application. - + # (Required) description: | A comprehensive overview of your Flask application. - + extensions: - flask-framework - + # Uncomment the integrations used by your application # requires: # mysql: @@ -87,11 +89,13 @@ extension. # limit: 1 -To view details about what that extension is adding to your charm, set the ``CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS`` to ``1``, then run ``charmcraft expand-extensions``. For example: +To view details about what that extension is adding to your charm, set the +``CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS`` environment variable to ``1``, +then run ``charmcraft expand-extensions``. For example: .. code:: text - + CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions @@ -99,9 +103,9 @@ To view details about what that extension is adding to your charm, set the ``CHA .. code:: text - + $ CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=1 charmcraft expand-extensions - *EXPERIMENTAL* extension 'flask-framework' enabled + *EXPERIMENTAL* extension 'flask-framework' enabled name: my-flask-app-k8s summary: A very short one-line summary of the flask application. description: | @@ -169,7 +173,7 @@ To view details about what that extension is adding to your charm, set the ``CHA flask-application-root: type: string description: Path in which the application / web server is mounted. This configuration - will set the FLASK_APPLICATION_ROOT environment variable. Run `app.config.from_prefixed_env()` + will set the FLASK_APPLICATION_ROOT environment variable. Run app.config.from_prefixed_env() in your Flask application in order to receive this configuration. flask-debug: type: boolean @@ -181,27 +185,27 @@ To view details about what that extension is adding to your charm, set the ``CHA type: int description: Time in seconds for the cookie to expire in the Flask application permanent sessions. This configuration will set the FLASK_PERMANENT_SESSION_LIFETIME - environment variable. Run `app.config.from_prefixed_env()` in your Flask application + environment variable. Run app.config.from_prefixed_env() in your Flask application in order to receive this configuration. flask-preferred-url-scheme: type: string default: HTTPS description: Scheme for generating external URLs when not in a request context in the Flask application. By default, it's "HTTPS". This configuration will - set the FLASK_PREFERRED_URL_SCHEME environment variable. Run `app.config.from_prefixed_env()` + set the FLASK_PREFERRED_URL_SCHEME environment variable. Run app.config.from_prefixed_env() in your Flask application in order to receive this configuration. flask-secret-key: type: string description: The secret key used for securely signing the session cookie and for any other security related needs by your Flask application. This configuration - will set the FLASK_SECRET_KEY environment variable. Run `app.config.from_prefixed_env()` + will set the FLASK_SECRET_KEY environment variable. Run app.config.from_prefixed_env() in your Flask application in order to receive this configuration. flask-session-cookie-secure: type: boolean description: Set the secure attribute in the Flask application cookies. This configuration will set the FLASK_SESSION_COOKIE_SECURE environment variable. - Run `app.config.from_prefixed_env()` in your Flask application in order to - receive this configuration. + Run app.config.from_prefixed_env() in your Flask application in order to + receive this configuration. .. diff --git a/docs/howto/manage-icons.rst b/docs/howto/manage-icons.rst index c9777a185..3703b4ecf 100644 --- a/docs/howto/manage-icons.rst +++ b/docs/howto/manage-icons.rst @@ -1,4 +1,5 @@ .. _manage-icons: + How to manage icons =================== @@ -73,10 +74,16 @@ page checks the most basic issues that prevent icons working. Add an icon to its charm's Charmhub page ---------------------------------------- -To add the icon to the charm's Charmhub page, save it as ``icon.svg``, place it in the root directory of the charm, and then publish the charm to a channel of the form ``/stable`` (e.g., ``latest/stable``). +To add the icon to the charm's Charmhub page, save it as ``icon.svg``, place it +in the root directory of the charm, and then publish the charm to a channel of the +form ``/stable`` (e.g., ``latest/stable``). .. note:: - That is because Charmhub only updates the metadata for a charm on stable channel releases (`by design `_). So either release the revision with the icon to a ``stable`` channel and then roll it back, or wait until your charm is ready for a "stable" ``stable`` release. + That is because Charmhub only updates the metadata for a charm on stable channel + releases (`by design + `_). + So either release the revision with the icon to a ``stable`` channel and then + roll it back, or wait until your charm is ready for a "stable" ``stable`` release. .. diff --git a/docs/howto/manage-libraries.rst b/docs/howto/manage-libraries.rst index 6cccd2ad7..a64e6a290 100644 --- a/docs/howto/manage-libraries.rst +++ b/docs/howto/manage-libraries.rst @@ -1,4 +1,5 @@ .. _manage-libraries: + How to manage libraries ======================= @@ -18,8 +19,9 @@ In your charm’s root directory, run ``charmcraft create-lib``: $ charmcraft create-lib demo .. note:: - Before creating a library, you must first register ownership of your charm’s name. See more: :ref:`publish-a-charm`. - + Before creating a library, you must first register ownership of your charm’s + name. See more: :ref:`publish-a-charm`. + This will create a template file at ``$CHARMDIR/lib/charms/demo/v0/demo.py``. @@ -28,53 +30,72 @@ This will create a template file at ``$CHARMDIR/lib/charms/demo/v0/demo.py``. Edit this file to write your library. .. important:: - A library must comprise a single Python file. If you write a library that feels too "big" for a single file, it is likely that the library should be split up, or that you are actually writing a full-on charm. + A library must comprise a single Python file. If you write a library that + feels too "big" for a single file, it is likely that the library should be + split up, or that you are actually writing a full-on charm. .. See next: `Ops \| Manage libraries `_ .. _publish-a-library: + Publish a library on Charmhub ----------------------------- .. caution:: - On Charmhub, a library is always associated with the charm that it was first created for. When you publisht it to Charmhub, it’s published to the page of that charm. To be able to publish it, you need to be logged in to Charmhub as a user who owns the charm (see more: :ref:`publish-a-charm`) or as a user who is registered as a contributor to the charm (a status that can be requested via `Discourse `_). + On Charmhub, a library is always associated with the charm that it was first + created for. When you publisht it to Charmhub, it’s published to the page of + that charm. To be able to publish it, you need to be logged in to Charmhub as + a user who owns the charm (see more: :ref:`publish-a-charm`) or as a user who + is registered as a contributor to the charm (a status that can be requested via + `Discourse`_). -To publish a library on Charmhub, in the root directory of the charm that holds the library, run `charmcraft publish-lib` followed by the full library path on the template `charms..v.`. For example: +To publish a library on Charmhub, in the root directory of the charm that holds +the library, run ``charmcraft publish-lib`` followed by the full library path on +the template ``charms..v.``. For example: .. code:: text - + $ charmcraft publish-lib charms.demo.v0.demo Library charms.demo.v0.demo sent to the store with version 0.1 -.. - - See more: :ref:`ref_commands_publish-lib` - This will upload the library’s content to Charmhub. -To update the library on Charmhub, update the ``LIBAPI``/``LIBPATCH`` metadata fields inside the library file, then repeat the publish procedure. +To update the library on Charmhub, update the ``LIBAPI``/``LIBPATCH`` metadata +fields inside the library file, then repeat the publish procedure. See more: :ref:`ref_commands_publish-lib` .. caution:: **About the metadata fields:** - Most times it is enough to just increment ``LIBPATCH`` but, if you’re introducing breaking changes, you must work with the major API version. - Additionally, be mindful of the fact that users of your library will update it automatically to the latest PATCH version with the same API version. To avoid breaking other people’s library usage, make sure toincrement the ``LIBAPI`` version but reset ``LIBPATCH`` to ``0``. Also, before adding the breaking changes and updating these values, make sureto copy the library to the new path; this way you can maintain different major API versions independently, being able to update, for example, your v0 after publishing v1. See more: :ref:`file-libname-py`. + Most times it is enough to just increment ``LIBPATCH`` but, if you’re introducing + breaking changes, you must work with the major API version. + Additionally, be mindful of the fact that users of your library will update it + automatically to the latest PATCH version with the same API version. To avoid + breaking other people’s library usage, make sure toincrement the ``LIBAPI`` + version but reset ``LIBPATCH`` to ``0``. Also, before adding the breaking + changes and updating these values, make sureto copy the library to the new path; + this way you can maintain different major API versions independently, being able + to update, for example, your v0 after publishing v1. + See more: :ref:`file-libname-py`. -.. +.. -To share your library with other charm developers, navigate to the host charm's Charmhub page, go to Libraries tab, then copy and share the URL at the top of the page. +To share your library with other charm developers, navigate to the host charm's +Charmhub page, go to Libraries tab, then copy and share the URL at the top of the +page. View the libs published for a charm ----------------------------------- -The easiest way to find an existing library for a given charm is via `charmcraft list-lib`, as shown below. This will query Charmhub and show which libraries are published for the specified charm, along with API/patch versions. +The easiest way to find an existing library for a given charm is via +``charmcraft list-lib``, as shown below. This will query Charmhub and show which +libraries are published for the specified charm, along with API/patch versions. .. code:: @@ -82,30 +103,35 @@ The easiest way to find an existing library for a given charm is via `charmcraft Library name API Patch superlib 1 0 -The listing will not show older API versions; this ensures that new users always start with the latest version. +The listing will not show older API versions; this ensures that new users always start +with the latest version. -Another good way to search for libraries is to explore the charm collection on [Charmhub](https://charmhub.io/). +Another good way to search for libraries is to explore the charm collection on +`Charmhub`_. - See more: :ref:`ref_commands_list-lib>` + See more: :ref:`ref_commands_list-lib` Use a library ------------- -In your charm's `charmcraft.yaml`, specify the `charm-libs` key with the desired libraries. +In your charm's ``charmcraft.yaml``, specify the ``charm-libs`` key with the +desired libraries. See more: :ref:`file-charmcraft-yaml-charm-libs` -In your charm's root directory, run `charmcraft fetch-libs`. Charmcraft will download the libraries to your charm's directory. +In your charm's root directory, run ``charmcraft fetch-libs``. Charmcraft will +download the libraries to your charm's directory. See more: :ref:`ref_commands_fetch-libs` -To use a library in your `src/charm.py`, import it using its fully-qualified path minus the `lib` part: +To use a library in your ``src/charm.py``, import it using its fully-qualified +path minus the ``lib`` part: .. code:: python - + import charms.demo.v0.demo To update your lib with the latest published version, repeat the process. diff --git a/docs/howto/manage-names.rst b/docs/howto/manage-names.rst index 89b4cb7bc..8ab44ba51 100644 --- a/docs/howto/manage-names.rst +++ b/docs/howto/manage-names.rst @@ -1,12 +1,15 @@ .. _manage-names: + How to manage names =================== .. _register-a-name: + Register a name on Charmhub --------------------------- -To register a name for your charm on Charmhub, use the ``chamrcraft register`` command followed by your desired name. E.g., +To register a name for your charm on Charmhub, use the ``charmcraft register`` +command followed by your desired name. E.g., .. code:: text diff --git a/docs/howto/manage-parts.rst b/docs/howto/manage-parts.rst index bf4e92ece..38a8ea17d 100644 --- a/docs/howto/manage-parts.rst +++ b/docs/howto/manage-parts.rst @@ -1,4 +1,5 @@ .. _manage-parts: + How to manage parts =================== diff --git a/docs/howto/manage-resources.rst b/docs/howto/manage-resources.rst index 6b0b30e8f..dbac97b16 100644 --- a/docs/howto/manage-resources.rst +++ b/docs/howto/manage-resources.rst @@ -1,21 +1,27 @@ .. _manage-resources: + How to manage resources ======================= - See first: `Juju \| Charm resource `_, `Juju \| - Manage resources `_ + See first: `Juju \| Charm resource `_, + `Juju \| Manage resources `_ Declare a resource ------------------ -To declare a resource required by your charm, in your charm’s ``charmcraft.yaml file`` specify the ``resources`` key. +To declare a resource required by your charm, in your charm’s +:doc:`/reference/files/file-charmcraft-yaml` specify the ``resources`` key. See more: :ref:`file-charmcraft-yaml-resources` - See next: `Ops \| Manage resources `_ + See next: + :external+ops:doc:`Ops \| How to manage resources ` .. tip:: - During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. For example, assuming the resource is a ``/tmp/somefile.txt`` file, you could pack and the deploy with ``juju deploy … –resource``: + During development, it may be useful to specify the resource at deploy time to + facilitate faster testing without the need to publish a new charm/resource in + between minor fixes. For example, assuming the resource is a ``/tmp/somefile.txt`` + file, you could pack and the deploy with ``juju deploy … –-resource``: .. code:: text @@ -25,15 +31,22 @@ To declare a resource required by your charm, in your charm’s ``charmcraft.yam .. _publish-a-resource: + Publish a resource on Charmhub ------------------------------ -.. note: You must have already published the charm. See more: :ref:`publish-a-charm`. +.. note:: You must have already published the charm. See more: :ref:`publish-a-charm`. -To publish a resource on its charm's Charmhub page, run the ``charmcraft upload-resource`` command followed by the name of the charm, the name of the resource (cf. ``charmcraft.yaml``), and ``--filepath=`` / ``--image=``. For example: +To publish a resource on its charm's Charmhub page, run ``charmcraft upload-resource`` +followed by the name of the charm, the name of the resource (cf. ``charmcraft.yaml``), +and ``--filepath=`` / ``--image=``. For example: .. note:: - The option ``--image`` must indicate an OCI image's digest, being it in the short or long form (e.g.: ``70aa8983ec5c`` or ``sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249``). When using the "short form" of the digest, the image needs to be present locally so its proper ID (the "long form") can be retrieved. + The option ``--image`` must indicate an OCI image's digest, being it in the + short or long form (e.g.: ``70aa8983ec5c`` or + ``sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249``). + When using the "short form" of the digest, the image needs to be present + locally so its proper ID (the "long form") can be retrieved. .. code:: text @@ -45,9 +58,14 @@ To publish a resource on its charm's Charmhub page, run the ``charmcraft upload- $ charmcraft upload-resource my-super-charm redis-image --image=sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249 Revision 1 created of resource 'redis-image' for charm 'my-super-charm' -Charmcraft will first check if that specific image is available in Canonical’s Registry, and just use it if that’s the case. If not, it will try to get it from the developer’s local OCI repository (needs ``dockerd`` to be installed and running), push it to the Canonical’s Registry, and then use it. Either way, when the upload has completed, you end up with a resource revision. +Charmcraft will first check if that specific image is available in Canonical’s +Registry, and just use it if that’s the case. If not, it will try to get it from +the developer’s local OCI repository (needs ``dockerd`` to be installed and +running), push it to the Canonical’s Registry, and then use it. Either way, when +the upload has completed, you end up with a resource revision. -To update a pre-uploaded resource, run the ``upload-resource`` command again. The result will be a new revision. +To update a pre-uploaded resource, run the ``upload-resource`` command again. +The result will be a new revision. See more: :ref:`ref_commands_upload-resource` @@ -61,7 +79,7 @@ To view all the resources published on Charmhub for a charm, run The command will open up a browser window and ask you to log in. .. code:: text - + $ charmcraft resources mycharm .. @@ -69,6 +87,7 @@ To view all the resources published on Charmhub for a charm, run See more: :ref:`ref_commands_resources` .. _manage-resource-revisions: + Manage resource revisions ------------------------- diff --git a/docs/howto/manage-revisions.rst b/docs/howto/manage-revisions.rst index 5f044dc35..62cb4ca9b 100644 --- a/docs/howto/manage-revisions.rst +++ b/docs/howto/manage-revisions.rst @@ -1,4 +1,5 @@ .. _manage-charm-revisions: + How to manage charm revisions ============================= @@ -25,13 +26,14 @@ followed by the name of the charm. Promote a charm revision to a better risk level ----------------------------------------------- -To promote a charm revision to a higher-ranking risk level, use the GitHub ``promote-charm`` action. +To promote a charm revision to a higher-ranking risk level, use the GitHub +``promote-charm`` action. - See more: `GitHub \| - canonical/charming-actions/promote-charm `__ + See more: `GitHub | canonical/charming-actions/promote-charm + `_ .. dropdown:: Example outcome - + For example, in the (partial) output of juju info mongodb below, revision 100 has been promoted from ``3.6/edge`` through ``3.6/beta`` and ``3.6/candidate`` all the way to ``3.6/stable``. (The up arrow next @@ -39,9 +41,9 @@ To promote a charm revision to a higher-ranking risk level, use the GitHub ``pro try ``juju deploy --channel 3.6/beta``, what you’ll get is the next higher-ranking risk level of the same track, that is, ``3.6/candidate``.) - + .. code:: text - + channels: | 5/stable: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 5/candidate: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 @@ -55,11 +57,12 @@ To promote a charm revision to a higher-ranking risk level, use the GitHub ``pro .. _release-a-revision-into-a-channel: + Release a charm revision into a channel --------------------------------------- -To release a specific charm revision to a channel, run ``charmcraft release`` followed by the name of the charm and flags -specifying the revision and its target channel. E.g., +To release a specific charm revision to a channel, run ``charmcraft release`` followed +by the name of the charm and flags specifying the revision and its target channel. E.g., .. code:: text @@ -74,6 +77,11 @@ This opens the channel you’re releasing to. See more: :ref:`manage-channels` -Following the release, Charmhub will display the charm’s information at ``charmhub.io/``. (The default information displayed is obtained from the most stable channel.) Your charm will also become available for download. +Following the release, Charmhub will display the charm’s information at +``charmhub.io/``. (The default information displayed is obtained from the +most stable channel.) Your charm will also become available for download. + + See more: `Juju | Manage charms`_ + - See more: `Juju \| Manage charms `_ +.. _`Juju | Manage charms`: https://juju.is/docs/juju/manage-charms-or-bundles diff --git a/docs/howto/manage-the-charmcraft-cli.rst b/docs/howto/manage-the-charmcraft-cli.rst index d4a96cefd..9ca1d52a7 100644 --- a/docs/howto/manage-the-charmcraft-cli.rst +++ b/docs/howto/manage-the-charmcraft-cli.rst @@ -1,4 +1,5 @@ .. _manage-the-charmcraft-cli: + How to manage the ``charmcraft`` CLI ==================================== @@ -44,9 +45,11 @@ You can also install Charmcraft in an isolated environment. On macOS ~~~~~~~~ -Charmcraft is `available on homebrew `_. +An unofficial Charmcraft package is +`available on homebrew `_. -Installation should be straightforward if using homebrew (if not already set up, refer to `these instructions `_). +Installation should be straightforward if using homebrew (if not already set up, +refer to `these instructions `_). .. code:: text @@ -68,7 +71,9 @@ Charmhub commands work natively: username: jdoe id: xxxxxxxxxxxxxxxxxxxxxxxxx -In macOS, Charmcraft defaults to Multipass to build the charms in a container matching the target base(s). Running pack asks to setup Multipass if not already installed, and continues with the packing process: +In macOS, Charmcraft defaults to Multipass to build the charms in a container +matching the target base(s). Running pack asks to setup Multipass if not already +installed, and continues with the packing process: .. code:: text @@ -98,15 +103,18 @@ please refer to how to install it in an `isolated environment <#heading--isolated>`_. .. _install-in-an-isolated-environment: + In an isolated environment on Linux, macOS, or Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Another way to install Charmcraft is via `Multipass `_. This is a good way to install it -on any platform, as it will give you an isolated development environment. +Another way to install Charmcraft is via `Multipass`_. +This is a good way to install it on any platform, as it will give you an isolated +development environment. First, `install Multipass `_. -Second, use Multipass to provision a virtual machine. The following command will launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name ‘charm-dev’: +Second, use Multipass to provision a virtual machine. The following command will +launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name ‘charm-dev’: .. code:: text diff --git a/docs/howto/manage-the-current-charmhub-user.rst b/docs/howto/manage-the-current-charmhub-user.rst index bcaaf3541..e0e55fa59 100644 --- a/docs/howto/manage-the-current-charmhub-user.rst +++ b/docs/howto/manage-the-current-charmhub-user.rst @@ -1,8 +1,9 @@ .. _manage-the-current-charmhub-user: + How to manage the current Charmhub user ======================================= - See first: `Charmhub `_ + See first: `Charmhub`_ Log in to Charmhub ------------------ @@ -13,7 +14,7 @@ Local environments To log in to Charmhub, run ``charmcraft login``: .. code:: text - + $ charmcraft login Opening an authorization web page in your browser. If it does not open, please open this URL: @@ -112,7 +113,7 @@ CI/CD system that will push and release a charm could be: ... charmcraft upload my-super-charm.charm --release edge - + Check the currently logged in user ---------------------------------- diff --git a/docs/howto/manage-tracks.rst b/docs/howto/manage-tracks.rst index 91fae6098..210d5f210 100644 --- a/docs/howto/manage-tracks.rst +++ b/docs/howto/manage-tracks.rst @@ -1,8 +1,9 @@ .. _manage-tracks: + How to manage tracks ==================== -.. See also: :ref:`track` Add link to Juju docs > charm > channel > track +.. See also: :ref:`track` Add link to Juju docs > charm > channel > track When you register a charm name on Charmhub, you automatically get 4 channels, all with track ``latest``. However, as your charm evolves, @@ -11,6 +12,7 @@ with the workload) and then create new tracks in the new pattern. This document shows you how. .. _request-a-track-guardrail: + Request a track guardrail ------------------------- @@ -21,6 +23,7 @@ post on Discourse under the **charmhub requests** category, that is, here: :literalref:`https://discourse.charmhub.io/c/charmhub-requests`. .. _create-a-track: + Create a track -------------- @@ -44,10 +47,18 @@ Create it yourself To create a new track yourself, follow the steps below: .. important:: - As you might notice, this path is currently a little hacky. In the long-term it should become a lot smoother as there are plans to support it through the Charmcraft CLI. + As you might notice, this path is currently a little hacky. In the long-term it + should become a lot smoother as there are plans to support it through the + Charmcraft CLI. .. important:: - As you will see, this method currently relies on `charmcraft`+ `curl`. We recommend the Charmcraft bit because Charmcraft already understands the authentication mechanism used by Charmhub and can generate a suitable authentication token (macaroon) that will make it possible to then use `curl` directly to interact with the Charmhub API. This method also has the advantage that it can be adapted to use any HTTP client or library as long as it can pass custom headers. + As you will see, this method currently relies on ``charmcraft`` + ``curl``. + We recommend the Charmcraft bit because Charmcraft already understands the + authentication mechanism used by Charmhub and can generate a suitable + authentication token (macaroon) that will make it possible to then use ``curl`` + directly to interact with the Charmhub API. This method also has the advantage + that it can be adapted to use any HTTP client or library as long as it can pass + custom headers. 1. Enable ``curl`` access to the Charmhub API. @@ -57,40 +68,55 @@ First, install ``curl`` and ``jq``. You might already have both. -Then, use Charmcraft to log in to Charmhub and export your Charmhub credentials / token (macaroon) to a file: +Then, use Charmcraft to log in to Charmhub and export your Charmhub +credentials / token (macaroon) to a file: .. code:: text - + charmcraft login --export charmhub-creds.dat -Next, decode and extract the macaroon from the .dat file and place it in a header in an environment variable: +Next, decode and extract the macaroon from the .dat file and place it in a header +in an environment variable: .. code:: text export CHARMHUB_MACAROON_HEADER="Authorization: Macaroon $(cat charmhub-creds.dat | base64 -d | jq -r .v)" -At this point you can use this variable in ``curl`` commands – just make sure to specify the correct ``Content-Type``. +At this point you can use this variable in ``curl`` commands – just make sure to +specify the correct ``Content-Type``. -**2. Use ``curl`` to view the existing guardrails and tracks.** To view the guardrails and tracks associated with your charm, issue an HTTP ``GET`` request to ``/v1//``. For example, for a charm named ``hello-world-charm``: +**2. Use curl to view the existing guardrails and tracks.** To view the +guardrails and tracks associated with your charm, issue an HTTP ``GET`` request to +``/v1//``. For example, for a charm named ``hello-world-charm``: .. code:: text curl https://api.charmhub.io/v1/charm/hello-world-charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -The guardrails and tracks of the package will be under the ``track-guardrails`` and ``tracks`` keys of ``metadata``. Now you know -what the new track may look like. +The guardrails and tracks of the package will be under the ``track-guardrails`` +and ``tracks`` keys of ``metadata``. Now you know what the new track may look like. + + See more: `Charmhub API docs > package\_metadata + `_ + +.. important:: + + **If you want to view the guardrails and tracks for all published charms:** - See more: `Charmhub API docs > package\_metadata `_ + Issue an HTTP ``GET`` request to ``/v1/``, as below. + See more: `Charmhub API docs > list_registered_names + `_. -.. important:: **If you want to view the guardrails and tracks for all published charms:** - Issue an HTTP ``GET`` request to ``/v1/``, as below. See more: `Charmhub API docs > list_registered_names `_. + .. code:: text - .. code:: text - - curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" + curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -3. Use ``curl`` to create a new track. Finally, to create a new track for your charm, issue an HTTP ``POST`` request to ``/v1///tracks``, where ``name`` and ``namespace`` refer to the name and type of the package respectively. For example, given a charm named ``hello-world-charm``, one can create two tracks ``v.1`` and ``v.2`` as follows: +3. Use ``curl`` to create a new track. Finally, to create a new track for your + charm, issue an HTTP ``POST`` request to ``/v1///tracks``, + where ``name`` and ``namespace`` refer to the name and type of the package + respectively. For example, given a charm named ``hello-world-charm``, one can + create two tracks ``v.1`` and ``v.2`` as follows: .. code:: text @@ -99,6 +125,7 @@ what the new track may look like. Of course, the tracks must conform to the existing guardrail for the charm. - See more: `Charmhub API docs > create_tracks `_ + See more: `Charmhub API docs > create_tracks + `_ That’s it, you now have a new track for your charm! diff --git a/docs/howto/misc/index.rst b/docs/howto/misc/index.rst index a9b12eb28..d2ffd3a1f 100644 --- a/docs/howto/misc/index.rst +++ b/docs/howto/misc/index.rst @@ -1,4 +1,5 @@ -.. howto-misc: +.. _howto-misc: + Miscellaneous ============= @@ -9,4 +10,4 @@ Miscellaneous Migrate to python Cache intermediate build artefacts Pack a hook-based charm with Charmcraft - Pack a reactive-based charm with Charmcraft + Pack a reactive charm with Charmcraft diff --git a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst index 2ad7e499f..a3f952fc4 100644 --- a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst +++ b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.rst @@ -1,19 +1,25 @@ .. _pack-a-hooks-based-charm-with-charmcraft: + How to pack a hooks-based charm with Charmcraft =============================================== Introduced in Charmcraft 1.4 - See first: `Ops \| Turn a hooks-based charm into an Ops charm `_ - -Suppose you have a legacy hooks-only charm, for example, [tiny-bash](https://github.com/erik78se/tiny-bash), which you can obtain as follows: + See first: + :external+ops:doc:`Ops | Turn a hooks-based charm into an Ops charm + ` + +Suppose you have a legacy hooks-only charm, for example, +`tiny-bash `_, which you can obtain as follows: .. code:: text $ git clone https://github.com/erik78se/tiny-bash -To make it packable by Charmcraft, all you need to do is navigate inside the charm directory and create a ``charmcraft.yaml`` file with the part definition for a hooks-based charm, as shown below: +To make it packable by Charmcraft, all you need to do is navigate inside the charm +directory and create a ``charmcraft.yaml`` file with the part definition for a +hooks-based charm, as shown below: .. code:: yaml @@ -87,7 +93,8 @@ And you can also deploy your application with ``juju deploy``, as usual: Located local charm "tiny-bash", revision 0 Deploying "tiny-bash" from local charm "tiny-bash", revision 0 -If successful, the result should look as below, i.e., with the application status active. +If successful, the result should look as below, i.e., with the application status +active. .. code:: text @@ -103,4 +110,3 @@ If successful, the result should look as below, i.e., with the application statu Machine State DNS Inst id Series AZ Message 0 started 10.2.17.31 juju-55481c-0 focal Running - diff --git a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst b/docs/howto/misc/pack-a-reactive-charm-with-charmcraft.rst similarity index 67% rename from docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst rename to docs/howto/misc/pack-a-reactive-charm-with-charmcraft.rst index fc1f5112f..e64dbb7de 100644 --- a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.rst +++ b/docs/howto/misc/pack-a-reactive-charm-with-charmcraft.rst @@ -1,5 +1,6 @@ -.. _pack-a-reactive-based-charm-with-charmcraft: -How to pack a reactive-based charm with Charmcraft +.. _pack-a-reactive-charm-with-charmcraft: + +How to pack a reactive charm with Charmcraft ================================================== Introduced in Charmcraft 1.4. @@ -11,7 +12,8 @@ How to pack a reactive-based charm with Charmcraft .. {ref}\ ``How to pack your charm using Charmcraft `` .. - {ref}\ ``About charm types, by creation type `` -To pack a legacy reactive-based charm with Charmcraft, in the charm directory create a ``charmcraft.yaml`` file with the part definition for a reactive-based charm: +To pack a legacy reactive charm with Charmcraft, in the charm directory create a +``charmcraft.yaml`` file with the part definition for a reactive-based charm: .. code:: yaml @@ -29,5 +31,5 @@ To pack a legacy reactive-based charm with Charmcraft, in the charm directory cr plugin: reactive build-snaps: [charm] -Done. Now you can go ahead and pack your reactive-based charm with Charmcraft in the usual way using ``charmcraft pack``. - +Done. Now you can go ahead and pack your reactive-based charm with Charmcraft +in the usual way using ``charmcraft pack``. diff --git a/docs/reference/charmcraft-cli.rst b/docs/reference/charmcraft-cli.rst index c13477afe..8206fbf19 100644 --- a/docs/reference/charmcraft-cli.rst +++ b/docs/reference/charmcraft-cli.rst @@ -1,10 +1,9 @@ .. _charmcraft-cli: + ``charmcraft`` CLI ================== .. toctree:: :maxdepth: 2 - - list-of-charmcraft-cli-commands - + list-of-charmcraft-cli-commands diff --git a/docs/reference/files/file-bundle-yaml.rst b/docs/reference/files/file-bundle-yaml.rst index 4fd2ae9e0..58029aa8c 100644 --- a/docs/reference/files/file-bundle-yaml.rst +++ b/docs/reference/files/file-bundle-yaml.rst @@ -1,4 +1,5 @@ .. _file-bundle-yaml: + .. highlight:: yaml ``bundle.yaml`` @@ -8,19 +9,28 @@ Source for the keys used by Juju: `Schema `_, - `Examples from test files `_ + `Examples from test files + `_ - (The metadata keys ``docs``, ``issues``, ``source``, and ``website`` are only used by Charmhub. + (The metadata keys ``docs``, ``issues``, ``source``, and ``website`` are + only used by Charmhub. -File ``.yaml`` is the file in your bundle directory where you define your bundle. +File ``.yaml`` is the file in your bundle directory where you define +your bundle. .. important:: - ``bundle.yaml`` is typically generated using `Juju's export-bundle command `_. + ``bundle.yaml`` is typically generated using + `Juju's export-bundle command `_. **For overlay bundles:** -- Instead of providing overlays as external files, you may alternatively leverage Juju's support for multi-document YAML files and provide both the base overlay and any required overlays as a *single file*, appending the contents of the overlay after the base bundle using the special YAML document separator token `---` as the delimiter. Juju will treat the first document as the base bundle and any subsequent document as an overlay. +- Instead of providing overlays as external files, you may alternatively leverage + Juju's support for multi-document YAML files and provide both the base overlay + and any required overlays as a *single file*, appending the contents of the + overlay after the base bundle using the special YAML document separator token + ``---`` as the delimiter. Juju will treat the first document as the base bundle + and any subsequent document as an overlay. .. dropdown:: Example base and overlay in the same file @@ -41,10 +51,18 @@ File ``.yaml`` is the file in your bundle directory where you define you mysql: trust: true -- Relative paths are resolved relative to the path of the entity that describes them. That is, relative to the overlay bundle file itself. -- An application is removed from the base bundle by defining the application name in the application section, but omitting any values. Removing an application also removes all the relations for that application. -- If a machines section is specified in an overlay bundle, it replaces the corresponding section of the base bundle. No merging of machine information is attempted. Multiple overlay bundles can be specified and they are processed in the order they appear on the command line. -- Overlays can include new integrations, which are normally required for any new charms which have been added. Existing integrations cannot be removed however, except in the case where the referenced application is also removed by the overlay. +- Relative paths are resolved relative to the path of the entity that describes them. + That is, relative to the overlay bundle file itself. +- An application is removed from the base bundle by defining the application name + in the application section, but omitting any values. Removing an application also + removes all the relations for that application. +- If a machines section is specified in an overlay bundle, it replaces the + corresponding section of the base bundle. No merging of machine information is + attempted. Multiple overlay bundles can be specified and they are processed in + the order they appear on the command line. +- Overlays can include new integrations, which are normally required for any new + charms which have been added. Existing integrations cannot be removed however, + except in the case where the referenced application is also removed by the overlay. .. dropdown:: Example ``bundle.yaml`` file -- Kubernetes @@ -175,13 +193,15 @@ The rest of this document describes each key in this file. .. note:: - A bundle for deployment on Kubernetes differs from a standard bundle in the following ways: + A bundle for deployment on Kubernetes differs from a standard bundle in the + following ways: - key ``bundle`` is given the value of ``kubernetes`` - key ``num_units`` is replaced by key ``scale`` - key ``to`` is replaced by key ``placement`` - The value of ``placement`` is a key=value pair and is used as a Kubernetes node selector. + The value of ``placement`` is a key=value pair and is used as a Kubernetes + node selector. ``applications`` @@ -196,7 +216,8 @@ The rest of this document describes each key in this file. **Purpose:** Holds an application definition. -**Name:** The name of the application. User-defined, usually identical to `applications..charm`_ +**Name:** The name of the application. User-defined, usually identical to +`applications..charm`_ ``applications..annotations`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -235,9 +256,13 @@ that is not explicitly bound to a space. ``applications..channel`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Purpose:** States what the preferred channel should be used when deploying a non-local charm. +**Purpose:** States what the preferred channel should be used when deploying a +non-local charm. + +.. note:: -.. note:: Charmhub charms expect ``//`` format (e.g., ``latest/stable``). + Charmhub charms expect ``//`` format (e.g., + ``latest/stable``). .. dropdown:: Example @@ -248,7 +273,9 @@ that is not explicitly bound to a space. ``applications..charm`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Purpose:** States what charm to use for the application. **If you're defining a public bundle:** Use a fully qualified charm URI. +**Purpose:** States what charm to use for the application. + +**If you're defining a public bundle:** Use a fully qualified charm URI. .. dropdown:: Example @@ -412,7 +439,7 @@ permission levels. user1: read ``applications..options`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sets configuration options for the application. The keys are application-specific and are found within the corresponding charm's metadata.yaml file. An alias (a string @@ -788,9 +815,10 @@ using rules of precedence (most preferred to least): - the series stated for a machine that an application unit has been assigned to (see `machines`_) -- the series stated for an application (see `series` under the `` element) -- the series given by the top level `series` element -- the top-most series specified in a charm's `metadata.yaml` file +- the series stated for an application (see ``series`` under the ``_ + element) +- the series given by the top level ``series`` element +- the top-most series specified in a charm's ``metadata.yaml`` file - the most recent LTS release .. dropdown:: Example @@ -805,7 +833,8 @@ using rules of precedence (most preferred to least): **Status:** Optional -**Purpose:** A string or list of strings containing a link (or links) to the bundle source code. +**Purpose:** A string or list of strings containing a link (or links) to the +bundle source code. ``tags`` ======== diff --git a/docs/reference/files/file-charmcraft-yaml.rst b/docs/reference/files/file-charmcraft-yaml.rst index 4445a50f3..0ecfb98c7 100644 --- a/docs/reference/files/file-charmcraft-yaml.rst +++ b/docs/reference/files/file-charmcraft-yaml.rst @@ -1,4 +1,5 @@ .. _file-charmcraft-yaml: + .. highlight:: yaml ``charmcraft.yaml`` @@ -16,21 +17,29 @@ This test file shows the full spec at once: https://github.com/canonical/charmcraft/blob/main/tests/unit/models/valid_charms_yaml/full.yaml .. important:: - ``charmcraft.yaml`` file is the only ``yaml`` file generated by ``charmcraft init`` and the only ``yaml`` file in a charm project that a charm author should edit directly. + ``charmcraft.yaml`` file is the only ``yaml`` file generated by ``charmcraft init`` + and the only ``yaml`` file in a charm project that a charm author should edit + directly. - Charmcraft will use the information you provide here to generate :ref:`file-actions-yaml`, :ref:`file-config-yaml`, and :ref:`file-metadata-yaml`, as well as all the other files it usually does. + Charmcraft will use the information you provide here to generate + :doc:`file-actions-yaml`, :doc:`file-config-yaml`, and :doc:`file-metadata-yaml`, + as well as all the other files it usually does. -``charmcraft.yaml`` is a file in your charm project that contains keys that allow you to declare information about the project in a form that can be used by Charmcraft. +``charmcraft.yaml`` is a file in your charm project that contains keys that allow you +to declare information about the project in a form that can be used by Charmcraft. .. note:: - If you're starting from an empty file, the only required key is the ``type`` key. However, depending on what value you set it to (``charm`` or ``bundle``), other keys become required as well. + If you're starting from an empty file, the only required key is the ``type`` key. + However, depending on what value you set it to (``charm`` or ``bundle``), other + keys become required as well. .. dropdown:: Expand to view a full charm with sample content all at once .. literalinclude:: charmcraft-sample-charm.yaml -.. _file-charmcraft-yaml-actions:: +.. _file-charmcraft-yaml-actions: + ``actions`` =========== @@ -44,7 +53,7 @@ **Value:** Mapping. -The value of this key is the contents of :ref:`file-actions-yaml`. +The value of this key is the contents of :doc:`file-actions-yaml`. .. dropdown:: Example @@ -53,7 +62,8 @@ The value of this key is the contents of :ref:`file-actions-yaml`. :end-before: analysis: -.. _file-charmcraft-yaml-analysis:: +.. _file-charmcraft-yaml-analysis: + ``analysis`` ============ @@ -61,7 +71,9 @@ The value of this key is the contents of :ref:`file-actions-yaml`. **Status:** Optional. -**Purpose:** Defines how the analysis done on the package will behave. This analysis is run implicitly as part of the ``pack`` command but can be called explicitly with the ``charmcraft analyse`` command. +**Purpose:** Defines how the analysis done on the package will behave. This analysis +is run implicitly as part of the ``pack`` command but can be called explicitly with +the ``charmcraft analyse`` command. **Structure:** @@ -78,15 +90,28 @@ The value of this key is the contents of :ref:`file-actions-yaml`. :start-at: analysis: :end-before: assumes: -.. _file-charmcraft-yaml-assumes:: +.. _file-charmcraft-yaml-assumes: + ``assumes`` =========== **Status:** Optional. Recommended for Kubernetes charms. -**Purpose:** Allows charm authors to explicitly state in the metadata of a charm various features that a Juju model must be able to provide to ensure that the charm can be successfully deployed on it. When a charm comes preloaded with such requirements, this enables Juju to perform a pre-deployment check and to display user-friendly error messages if a feature requirement cannot be met by the model that the user is trying to deploy the charm to. If the assumes section of the charm metadata is omitted, Juju will make a best-effort attempt to deploy the charm, and users must rely on the output of `juju status` to figure out whether the deployment was successful. The `assumes` key is available since 2.9.23. - -**Structure:** The key consists of a list of features that can be given either directly or, depending on the complexity of the condition you want to enforce, nested under one or both of the boolean expressions ``any-of`` or ``all-of``, as shown below. In order for a charm to be deployed, all entries in the ``assumes` block must be satisfied. +**Purpose:** Allows charm authors to explicitly state in the metadata of a charm +various features that a Juju model must be able to provide to ensure that the charm +can be successfully deployed on it. When a charm comes preloaded with such +requirements, this enables Juju to perform a pre-deployment check and to display +user-friendly error messages if a feature requirement cannot be met by the model +that the user is trying to deploy the charm to. If the assumes section of the charm +metadata is omitted, Juju will make a best-effort attempt to deploy the charm, and +users must rely on the output of ``juju status`` to figure out whether the deployment +was successful. + +**Structure:** The key consists of a list of features that can be given either +directly or, depending on the complexity of the condition you want to enforce, +nested under one or both of the boolean expressions ``any-of`` or ``all-of``, as +shown below. In order for a charm to be deployed, all entries in the ``assumes`` +block must be satisfied. .. code:: @@ -108,12 +133,14 @@ The value of this key is the contents of :ref:`file-actions-yaml`. - Examples - Juju versions * - ``juju `` - - The charm deploys iff the model runs agent binaries with the specified Juju version(s). + - The charm deploys iff the model runs agent binaries with the specified + Juju version(s). - ``juju >= 3.0`` ``juju < 4.0`` - Since 2.9.23 * - ``k8s-api`` - - The charm deploys iff the `backing cloud `_ for the model is Kubernetes. + - The charm deploys iff the `backing cloud `_ + for the model is Kubernetes. - ``k8s-api`` - Since Juju 2.9.23 @@ -131,11 +158,12 @@ The value of this key is the contents of :ref:`file-actions-yaml`. :start-at: assumes: :end-before: base: -.. _file-charmcraft-yaml-base:: +.. _file-charmcraft-yaml-base: + ``base`` ======== -**Status:** Required in most cases if :ref:`type` is ``charm``. +**Status:** Required in most cases if `type`_ is ``charm``. **Purpose:** Specifies the operating system on which the charm will build and run. @@ -153,12 +181,13 @@ The value of this key is the contents of :ref:`file-actions-yaml`. .. _file-charmcraft-yaml-bases: + ``bases`` ========= .. note:: - ``bases`` is deprecated, replaced by `base`_, `build-base`_, and `platforms`_. + ``bases`` is deprecated, replaced by `base`_, `build-base`_, and platforms. .. dropdown:: See more @@ -181,11 +210,15 @@ The value of this key is the contents of :ref:`file-actions-yaml`. build-on: | build-for: | -**Status:** Deprecated. Conflicts with the `base`_, `build-base`_, and `platforms`_ keys. Not allowed if `type`_ is ``bundle``. +**Status:** Deprecated. Conflicts with the `base`_, `build-base`_, and platforms +keys. Not allowed if `type`_ is ``bundle``. -**Purpose:** Specifies a list of environments (OS version and architecture) where the charm must be built on and run on. +**Purpose:** Specifies a list of environments (OS version and architecture) +where the charm must be built on and run on. -**Structure:** This key supports a list of bases where the charm can be built, and where that build can run. Each item can be expressed using two different internal structures, a short and a long form. The long one is more explicit: +**Structure:** This key supports a list of bases where the charm can be built, +and where that build can run. Each item can be expressed using two different +internal structures, a short and a long form. The long one is more explicit: .. code:: @@ -201,7 +234,9 @@ The value of this key is the contents of :ref:`file-actions-yaml`. architectures: - -The ``run-on`` part of each entry is optional and defaults to what's specified in the corresponding ``build-on``. In both structures the list of architecture strings is also optional, defaulting to the architecture of the current machine. +The ``run-on`` part of each entry is optional and defaults to what's specified +in the corresponding ``build-on``. In both structures the list of architecture +strings is also optional, defaulting to the architecture of the current machine. The short form is more concise and simple (at the cost of being less flexible): @@ -213,7 +248,9 @@ The short form is more concise and simple (at the cost of being less flexible): architectures: - -It implies that the specified base is to be used for both ``build-on`` and ``run-on``. As above, the list of architecture strings is also optional, defaulting to the machine architecture. +It implies that the specified base is to be used for both ``build-on`` and +``run-on``. As above, the list of architecture strings is also optional, defaulting +to the machine architecture. .. dropdown:: Example @@ -260,6 +297,7 @@ It implies that the specified base is to be used for both ``build-on`` and ``run - armhf .. _file-charmcraft-yaml-build-base: + ``build-base`` ============== @@ -280,12 +318,15 @@ It implies that the specified base is to be used for both ``build-on`` and ``run base: ubuntu@devel .. _file-charmcraft-yaml-charm-libs: + ``charm-libs`` ============== **Status:** Optional. -**Purpose:** Declares charm libraries for Charmcraft to include in the charm project. For each lib, include both the lib name (in ``.`` format) and the lib version (in ``"[.]"`` string format). +**Purpose:** Declares charm libraries for Charmcraft to include in the charm +project. For each lib, include both the lib name (in ``.`` format) +and the lib version (in ``"[.]"`` string format). **Structure:** @@ -302,20 +343,25 @@ It implies that the specified base is to be used for both ``build-on`` and ``run :end-before: config: .. _file-charmcraft-yaml-charmhub: + ``charmhub`` -===== +============ .. warning:: - This key is only meaningful in Charmcraft 2. Use the environment variables ``CHARMCRAFT_STORE_API_URL``, ``CHARMCRAFT_UPLOAD_URL`` and ``CHARMCRAFT_REGISTRY_URL`` for newer versions of Charmcraft. + This key is only meaningful in Charmcraft 2. Use the environment variables + ``CHARMCRAFT_STORE_API_URL``, ``CHARMCRAFT_UPLOAD_URL`` and + ``CHARMCRAFT_REGISTRY_URL`` for newer versions of Charmcraft. **Status:** Deprecated and nonfunctional in Charmcraft 3. **Purpose:** Configures Charmcraft's interaction with store servers. -**Structure:** This key allows for the configuration of three values---the base URL for the Charmhub API, the base URL to push binaries to Charmhub and the URL of the container registry for OCI image uploads. These keys are also optional. +**Structure:** This key allows for the configuration of three values---the base +URL for the Charmhub API, the base URL to push binaries to Charmhub and the URL +of the container registry for OCI image uploads. These keys are also optional. .. code:: @@ -324,7 +370,8 @@ It implies that the specified base is to be used for both ``build-on`` and ``run storage-url: registry-url: -The key is used mostly in the context of "private" charm stores, defaulting to the standard Canonical services to operate with charms. +The key is used mostly in the context of "private" charm stores, defaulting to +the standard Canonical services to operate with charms. .. dropdown:: Example @@ -335,8 +382,9 @@ The key is used mostly in the context of "private" charm stores, defaulting to t storage-url: https://storage.staging.snapcraftcontent.com .. _file-charmcraft-yaml-config: + ``config`` -========= +========== See first: `Juju | Application configuration `_ @@ -359,7 +407,8 @@ The key is used mostly in the context of "private" charm stores, defaulting to t # (Optional): A string describing the option. Also appears on charmhub.io description: -If ``type`` is ``secret``, this is a string that needs to correspond to the secret URI. +If ``type`` is ``secret``, this is a string that needs to correspond to the +secret URI. .. dropdown:: Example @@ -368,14 +417,32 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :end-before: containers: .. _file-charmcraft-yaml-containers: + ``containers`` ============== -**Status:** Required for Kubernetes charms (except for proxy charms running on Kubernetes). - -**Purpose:** The ``containers`` key allows you to define a map of containers to be created adjacent to the charm (as a sidecar, in the same pod). - -**Structure:** This key consists of a list of containers along with their specification. Each container can be specified in terms of ``resource``, ``bases``, ``uid``, ``gid`` and ``mounts``, where one of either the ``resource`` or the ``bases`` subkeys must be defined, and ``mounts`` is optional. ``resource`` stands for the OCI image resource used to create the container; to use it, specify an OCI image resource name (that you will then define further in the `resources`_ block). ``bases`` is a list of bases to be used for resolving a container image, in descending order of preference; to use it, specify a base name (for example, ``ubuntu``, ``centos``, ``windows``, ``osx``, ``opensuse``), a `channel `_, and an architecture. ``mounts`` is a list of mounted storages for this container; to use it, specify the name of the storage to mount from the charm storage and, optionally, the location where to mount the storage. Starting with Juju 3.5.0, ``uid`` and ``gid`` are the UID and, respectively, GID to run the Pebble entry process for this container as; they can be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved for users) and the default is 0 (root). +**Status:** Required for Kubernetes charms (except for proxy charms running on +Kubernetes). + +**Purpose:** The ``containers`` key allows you to define a map of containers to be +created adjacent to the charm (as a sidecar, in the same pod). + +**Structure:** This key consists of a list of containers along with their +specification. Each container can be specified in terms of ``resource``, ``bases``, +``uid``, ``gid`` and ``mounts``, where one of either the ``resource`` or the +``bases`` subkeys must be defined, and ``mounts`` is optional. ``resource`` stands +for the OCI image resource used to create the container; to use it, specify an OCI +image resource name (that you will then define further in the `resources`_ block). +``bases`` is a list of bases to be used for resolving a container image, in descending +order of preference; to use it, specify a base name (for example, ``ubuntu``, +``centos``, ``windows``, ``osx``, ``opensuse``), a +`channel `_, and an architecture. ``mounts`` is a +list of mounted storages for this container; to use it, specify the name of the +storage to mount from the charm storage and, optionally, the location where to mount +the storage. Starting with Juju 3.5.0, ``uid`` and ``gid`` are the UID and, +respectively, GID to run the Pebble entry process for this container as; they can +be any value from 0-999 or any value from 10,000 (values from 1000-9999 are reserved +for users) and the default is 0 (root). .. code:: @@ -400,10 +467,11 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :end-before: description: .. _file-charmcraft-yaml-description: + ``description`` =============== -**Status:** Required if the `type`_ key is set to `charm`. Recommended otherwise. +**Status:** Required if the `type`_ key is set to ``charm``. Recommended otherwise. **Example:** @@ -412,6 +480,7 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :end-before: devices: .. _file-charmcraft-yaml-devices: + ``devices`` =========== @@ -442,6 +511,7 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :end-before: extra-bindings: .. _file-charmcraft-yaml-extra-bindings: + ``extra-bindings`` ================== @@ -463,6 +533,13 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :end-before: links: .. _file-charmcraft-yaml-links: +.. _file-charmcraft-yaml-links-contact: +.. _file-charmcraft-yaml-links-source: +.. _file-charmcraft-yaml-links-issues: +.. _file-charmcraft-yaml-links-website: +.. _file-charmcraft-yaml-contact: +.. _file-charmcraft-yaml-documentation: + ``links`` ========= @@ -477,12 +554,16 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :end-before: name: .. _file-charmcraft-yaml-name: + ``name`` ======== **Status:** Required if the `type`_ key is set to ``charm``. -**Purpose:** The name of the charm. Determines the ``.charm`` file name and, if the charm is published on Charmhub, the charm page URL in Charmhub. As a result, it also determines the name administrators will ultimately use to deploy the charm. E.g. ``juju deploy ``. +**Purpose:** The name of the charm. Determines the ``.charm`` file name and, if the +charm is published on Charmhub, the charm page URL in Charmhub. As a result, it also +determines the name administrators will ultimately use to deploy the charm. E.g. +``juju deploy ``. **Structure:** @@ -496,15 +577,18 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr :start-at: name: :end-before: parts: -.. file-charmcraft-yaml-parts: +.. _file-charmcraft-yaml-parts: + ``parts`` ========= **Status:** Recommended. -**Purpose:** Configures the various mechanisms to obtain, process and prepare data from different sources that end up being a part of the final charm. +**Purpose:** Configures the various mechanisms to obtain, process and prepare +data from different sources that end up being a part of the final charm. -**Structure:** Mapping. Keys are user-defined part names. The value of each key is a map where keys are part properties. +**Structure:** Mapping. Keys are user-defined part names. The value of each key +is a map where keys are part properties. See more: :ref:`part_properties` @@ -576,6 +660,9 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr The ``reactive_charm_build_arguments`` allows to include extra command line arguments in the underlying ``charm build`` call. +.. _file-charmcraft-yaml-peers: +.. _file-charmcraft-yaml-provides: +.. _file-charmcraft-yaml-requires: ``peers``, ``provides``, ``requires`` ===================================== @@ -632,11 +719,13 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr **Structure:** -*Name:* Depending on what kind of an integration you are trying to define: ``peers``, ``provides``, or ``requires``. +*Name:* Depending on what kind of an integration you are trying to define: +``peers``, ``provides``, or ``requires``. *Type:* Map. -*Value:* One or more key-value pairs denoting a relation and its associated properties. +*Value:* One or more key-value pairs denoting a relation and its associated +properties. ``.`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -662,16 +751,24 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr *Type:* String. -*Value:* The name of the interface. Usually defined by the author of the charm providing the interface. Cannot be ``juju``. Cannot begin with ``juju-``. Must only contain characters `a-z` and `-` and cannot start with `-`. +*Value:* The name of the interface. Usually defined by the author of the charm +providing the interface. Cannot be ``juju``. Cannot begin with ``juju-``. Must +only contain characters ``a-z`` and ``-`` and cannot start with ``-``. + +.. warning:: -.. warning:: The interface name is the only means of establishing whether two charms are compatible for integration; and carries with it nothing more than a mutual promise that the provider and requirer know the communication protocol implied by the name. + The interface name is the only means of establishing whether two charms are + compatible for integration; and carries with it nothing more than a mutual + promise that the provider and requirer know the communication protocol implied + by the name. ``..limit`` ````````````````````````````````````````` **Status:** Optional. -**Purpose:** To define the maximum number of supported connections to this relation endpoint. +**Purpose:** To define the maximum number of supported connections to this relation +endpoint. **Structure:** @@ -695,7 +792,8 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr **Status:** Optional. -**Purpose:** To define the scope of the relation, that is, the set of units from integrated applications that are reported to the unit as members of the integration. +**Purpose:** To define the scope of the relation, that is, the set of units from +integrated applications that are reported to the unit as members of the integration. **Structure:** @@ -703,14 +801,18 @@ If ``type`` is ``secret``, this is a string that needs to correspond to the secr **Possible values:** ``container``, ``global``. -Container-scoped integrations are restricted to reporting details of a single principal unit to a single subordinate, and vice versa, while global integrations consider all possible remote units. Subordinate charms are only valid if they have at least one ``requires`` integration with ``container`` scope. +Container-scoped integrations are restricted to reporting details of a single +principal unit to a single subordinate, and vice versa, while global integrations +consider all possible remote units. Subordinate charms are only valid if they have +at least one ``requires`` integration with ``container`` scope. **Default value:** ``global``. .. _file-charmcraft-yaml-resources: + ``resources`` -===== +============= See first: `Juju | Charm resource `_ @@ -718,7 +820,9 @@ Container-scoped integrations are restricted to reporting details of a single pr **Purpose:** To define a `resource `_ for your charm. -.. note:: Kubernetes charms must declare an ``oci-image`` resource for each container they define in the `containers`_ mapping. +.. note:: + Kubernetes charms must declare an ``oci-image`` resource for each container + they define in the `containers`_ mapping. **Structure:** @@ -751,6 +855,7 @@ Container-scoped integrations are restricted to reporting details of a single pr .. _file-charmcraft-yaml-storage: + ``storage`` =========== @@ -808,12 +913,14 @@ Container-scoped integrations are restricted to reporting details of a single pr :end-before: subordinate: .. _file-charmcraft-yaml-subordinate: + ``subordinate`` =============== **Status:** Optional. -**Purpose:** Configures whether the charm is meant to be deployed as a subordinate to a principal charm. +**Purpose:** Configures whether the charm is meant to be deployed as a subordinate +to a principal charm. **Structure:** @@ -828,10 +935,11 @@ Container-scoped integrations are restricted to reporting details of a single pr subordinate: false .. _file-charmcraft-yaml-summary: + ``summary`` =========== -**Status:** Required if the `type`_ key is set to ``charm``. +**Status:** Required if the :ref:`file-charmcraft-yaml-type` key is set to ``charm``. **Structure:** A short, one-line description of the charm. No more than 78 characters. @@ -842,6 +950,7 @@ Container-scoped integrations are restricted to reporting details of a single pr summary: A Juju charm to run a Traefik-powered ingress controller on Kubernetes. .. _file-charmcraft-yaml-terms: + ``terms`` ========= @@ -863,6 +972,7 @@ Container-scoped integrations are restricted to reporting details of a single pr :end-before: title: My awesome charm .. _file-charmcraft-yaml-title: + ``title`` ========= @@ -877,6 +987,7 @@ Container-scoped integrations are restricted to reporting details of a single pr :end-before: type: .. _file-charmcraft-yaml-type: + ``type`` ======== diff --git a/docs/reference/files/file-config-yaml.rst b/docs/reference/files/file-config-yaml.rst index cf2ad46a6..7526e437a 100644 --- a/docs/reference/files/file-config-yaml.rst +++ b/docs/reference/files/file-config-yaml.rst @@ -1,4 +1,5 @@ .. _file-config-yaml: + .. highlight:: yaml ``config.yaml`` @@ -10,18 +11,23 @@ compatibility, Charmcraft will continue to allow the use of this file, but you may not duplicate keys across the two files. -The ``config.yaml`` in a charm's root directory is an optional file that may be used to define the configuration options supported by a charm. +The ``config.yaml`` in a charm's root directory is an optional file that may be used +to define the configuration options supported by a charm. -The definitions are collected under a single YAML map called ``options``. The rest of this doc gives details about this map. +The definitions are collected under a single YAML map called ``options``. The rest of +this doc gives details about this map. ``options`` =========== **Status:** Required if the file exists. -**Purpose:** The ``options`` key allows charm authors to declare the configuration options that they have defined for a charm. +**Purpose:** The ``options`` key allows charm authors to declare the configuration +options that they have defined for a charm. -**Structure:** The key contains a definition block for each option, where each definition consists of a charm-author-defined option name and an option description, given in 3 fields -- type, description, and default value: +**Structure:** The key contains a definition block for each option, where each +definition consists of a charm-author-defined option name and an option description, +given in 3 fields -- type, description, and default value: .. code:: @@ -36,7 +42,10 @@ The definitions are collected under a single YAML map called ``options``. The re type: ... -In some cases, it may be awkward or impossible to provide a sensible default. In these cases, ensure that it is noted in the description of the configuration option. It is acceptable to provide ``null`` configuration defaults or omit the ``default`` field. +In some cases, it may be awkward or impossible to provide a sensible default. +In these cases, ensure that it is noted in the description of the configuration +option. It is acceptable to provide ``null`` configuration defaults or omit the +``default`` field. .. dropdown:: Example diff --git a/docs/reference/files/file-dispatch.rst b/docs/reference/files/file-dispatch.rst index f4054e7b2..80616f611 100644 --- a/docs/reference/files/file-dispatch.rst +++ b/docs/reference/files/file-dispatch.rst @@ -24,7 +24,7 @@ will be deployed. .. dropdown:: Example charm with an overridden ``dispatch`` script - The following `part`_ section added to any charm will replace the dispatch + The following :ref:`part` section added to any charm will replace the dispatch script with a script that does nothing (but successfully) upon any event. .. code:: yaml diff --git a/docs/reference/files/file-libname-py.rst b/docs/reference/files/file-libname-py.rst index d207a9cc6..94d3ff012 100644 --- a/docs/reference/files/file-libname-py.rst +++ b/docs/reference/files/file-libname-py.rst @@ -124,7 +124,8 @@ virtual environment created in any charm that includes the library. Each string is a regular "pip installable" Python dependency that will be retrieved from PyPI in the usual way (subject to the user's system configuration) and which -supports all dependency formats (just the package name, a link to a Github project, etc.). +supports all dependency formats (just the package name, a link to a Github project, +etc.). .. dropdown:: Examples @@ -144,16 +145,17 @@ supports all dependency formats (just the package name, a link to a Github proje "requests", ] -Note that when called to install all the dependencies from the charm and all the used -libraries, `pip` may detect conflicts between the requested packages and their versions. -This is a feature, because it's always better to detect incompatibilities between -dependencies at this moment than when the charm is being deployed or run after -deployment. +Note that when called to install all the dependencies from the charm and all the +used libraries, ``pip`` may detect conflicts between the requested packages and +their versions. This is a feature, because it's always better to detect +incompatibilities between dependencies at this moment than when the charm is being +deployed or run after deployment. Code ^^^^ -After the docstring and the metadata, there's the library code. This is regular Python code. +After the docstring and the metadata, there's the library code. +This is regular Python code. Popular libraries ----------------- @@ -187,7 +189,8 @@ charm. - - Import RedisRequires from this lib to relate your charm to the `redis charm `_ - * - `grafana_dashboard `_ + * - `grafana_dashboard + `_ - - Defines a relation interface for charms that provide a dashboard to the `grafana-k8s charm `_ @@ -195,10 +198,11 @@ charm. - - Defines a relation interface for charms that serve as a data source for the `grafana-k8s charm `_ - * - `prometheus_scrape `_ + * - `prometheus_scrape + `_ - - - Defines a relation interface for charms that want to expose metrics endpoints to the - `prometheus charm `_. + - Defines a relation interface for charms that want to expose metrics endpoints + to the `prometheus charm `_. * - `alertmanager_dispatch `_ - @@ -206,13 +210,15 @@ charm. `_. * - `karma_dashboard `_ - - `karma-k8s `_ - - Defines an interface for charms wishing to consume or provide a karma-dashboard relation. - * - `loki_push_api `_ - - - `loki-k8s `_ + - Defines an interface for charms wishing to consume or provide a + karma-dashboard relation. + * - `loki_push_api + `_ + - - `loki-k8s `_ - Defines a relation interface for charms wishing to provide or consume the Loki Push API---e.g., a charm that wants to send logs to Loki. * - `log_proxy `_ - - - `loki-k8s `_ + - - `loki-k8s `_ - Defines a relation interface that allows a charm to act as a Log Proxy for Loki (via the Loki Push API). * - `guacd `_ @@ -235,7 +241,8 @@ or to perform operations common to several charms. * - `cert `_ - - `kubernetes-dashboard `_ - Generates a self signed certificate. - * - `capture_events `_ + * - `capture_events + `_ - - `traefik-k8s `_, - `data-platform-libs `_ - Helper for unit testing events. @@ -246,7 +253,8 @@ or to perform operations common to several charms. - - Provides utilities to track multiple independent statuses in charms. * - `resurrect `_ - - - `github-runner-image-builder `_ + - - `github-runner-image-builder + `_ - Provides utilities to periodically trigger charm hooks @@ -261,14 +269,16 @@ These libraries provide tooling for charms that run on top of Kubernetes clouds. * - Library - Used in - Description - * - `kubernetes_service_patch `_ + * - `kubernetes_service_patch + `_ - - `cos-configuration-k8s `_ - `alertmanager-k8s `_ - `grafana-agent-k8s `_ - `prometheus-k8s `_ - `loki-k8s `_ - `traefik-k8s `_ - - Allows charm authors to simply and elegantly define service overrides that persist through a charm upgrade. + - Allows charm authors to simply and elegantly define service overrides that + persist through a charm upgrade. * - `ingress `_ - - `nginx-ingress-integrator `_ - Configures nginx to use an existing Kubernetes Ingress. diff --git a/docs/reference/part.rst b/docs/reference/part.rst index c08930cdc..c661baaaa 100644 --- a/docs/reference/part.rst +++ b/docs/reference/part.rst @@ -1,4 +1,5 @@ .. _part: +.. _parts: **** Part @@ -17,4 +18,3 @@ system that charmcraft uses. /common/craft-parts/explanation/filesets lifecycle plugins/index - diff --git a/docs/reuse/links.txt b/docs/reuse/links.txt index 0e6be7ae9..dd1e4374c 100644 --- a/docs/reuse/links.txt +++ b/docs/reuse/links.txt @@ -3,6 +3,7 @@ .. _Chisel: https://github.com/canonical/chisel .. _`Chisel releases`: https://github.com/canonical/chisel-releases .. _`Craft-parts`: https://canonical-craft-parts.readthedocs-hosted.com +.. _Discourse: https://discourse.charmhub.io/ .. _Docker: https://docs.docker.com/ .. _Juju: https://juju.is/docs .. _`feature request`: https://github.com/canonical/charmcraft/issues/new?assignees=&labels=Enhancement&projects=&template=task.yaml diff --git a/docs/tutorial/flask.rst b/docs/tutorial/flask.rst index 37bfcc6bc..d17feca05 100644 --- a/docs/tutorial/flask.rst +++ b/docs/tutorial/flask.rst @@ -167,7 +167,8 @@ deployed in the Kubernetes cluster: .. seealso:: - See more: `skopeo `_ + See more: `Ubuntu manpage | skopeo + `_ Create the charm ================ @@ -520,4 +521,3 @@ Next steps - `SDK Explanation docs `_ ------------------------- - diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index 8932e9294..615f25a43 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -14,7 +14,7 @@ Our tutorial comes in multiple flavours -- pick your flavour of choice! .. toctree:: :maxdepth: 2 - Write your first Kubernetes charm for a Django app + write-your-first-kubernetes-charm-for-a-django-app flask - Write your first Kubernetes charm for a FastAPI app - Write your first Kubernetes charm for a Go app + write-your-first-kubernetes-charm-for-a-fastapi-app + write-your-first-kubernetes-charm-for-a-go-app From a4b774fd5c182ccb4d6d4fd4f376fd7b5167a135 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Thu, 19 Dec 2024 17:51:46 -0500 Subject: [PATCH 18/19] chore: make editorconfig match rst line length --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index cafe667ef..462f92677 100644 --- a/.editorconfig +++ b/.editorconfig @@ -32,7 +32,7 @@ max_line_length = off ij_visual_guides = none [{*.rst}] -max_line_length = off +max_line_length = 88 [{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] max_line_length = off From 3f07d2372479ac1e49ebd3ebb89b64ebaabc19f0 Mon Sep 17 00:00:00 2001 From: Alex Lowe Date: Fri, 20 Dec 2024 13:03:35 -0500 Subject: [PATCH 19/19] fix: pr suggestions Co-authored-by: Michael DuBelko --- docs/reference/files/file-charmcraft-yaml.rst | 8 ++++---- docs/reference/files/file-libname-py.rst | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/reference/files/file-charmcraft-yaml.rst b/docs/reference/files/file-charmcraft-yaml.rst index 0ecfb98c7..17be4b047 100644 --- a/docs/reference/files/file-charmcraft-yaml.rst +++ b/docs/reference/files/file-charmcraft-yaml.rst @@ -34,7 +34,7 @@ to declare information about the project in a form that can be used by Charmcraf keys become required as well. -.. dropdown:: Expand to view a full charm with sample content all at once +.. collapse:: Expand to view a full charm with sample content all at once .. literalinclude:: charmcraft-sample-charm.yaml @@ -348,7 +348,7 @@ and the lib version (in ``"[.]"`` string format). ============ -.. warning:: +.. caution:: This key is only meaningful in Charmcraft 2. Use the environment variables ``CHARMCRAFT_STORE_API_URL``, ``CHARMCRAFT_UPLOAD_URL`` and @@ -997,9 +997,9 @@ to a principal charm. **Structure:** -*Type:* String. +**Type:** String. -*Value:* ``charm`` or ``bundle``. +**Value:** ``charm`` or ``bundle``. .. dropdown:: Example diff --git a/docs/reference/files/file-libname-py.rst b/docs/reference/files/file-libname-py.rst index 94d3ff012..5e3458ed3 100644 --- a/docs/reference/files/file-libname-py.rst +++ b/docs/reference/files/file-libname-py.rst @@ -40,7 +40,7 @@ When you pack your charm, Charmcraft copies the top ``lib`` directory into the r directory of the charm. Thus, to import this library in Python use the full path minus the top ``lib`` directory, as below: -.. code:: text +.. code:: python import charms.mysql.v3.db

45(>|Si{Yz27Xp-zgw9x$}d>Idf4On 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 anEEdO1Mzd7ldWI?0000k{AR; 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 n8WU#puyLa%9p**i$_@zMP4e)Yn9aZbg+HXk/stable` (e.g., `latest/stable`). + +```{note} + +That is because Charmhub only updates the metadata for a charm on stable channel releases [(by design)](https://snapcraft.io/blog/better-snap-metadata-handling-coming-your-way-soon). +So either release the revision with the icon to a `stable` channel and then roll it back, or wait until your charm is ready for a "stable" `stable` release. + +``` + +> See more: {ref}`publish-a-charm` diff --git a/docs/howto/manage-libraries.md b/docs/howto/manage-libraries.md new file mode 100644 index 000000000..edfe1735c --- /dev/null +++ b/docs/howto/manage-libraries.md @@ -0,0 +1,111 @@ +(manage-libraries)= +# How to manage libraries +> See first: [`juju` | Library]() + + +## Initialise a library + +> See also: {ref}``charmcraft create-lib` ` + +In your charm's root directory, run `charmcraft create-lib`: + +```text +# Initialise a charm library named 'demo' +$ charmcraft create-lib demo +``` + +```{note} + +Before creating a library, you must first register ownership of your charm’s name. See more: {ref}`publish-a-charm`. + +``` + +This will create a template file at `$CHARMDIR/lib/charms/demo/v0/demo.py`. + + +> See more: {ref}`ref_commands_create-lib`, {ref}`file-libname-py` + +Edit this file to write your library. + +```{important} + +A library must comprise a single Python file. If you write a library that feels too "big" for a single file, it is likely that the library should be split up, or that you are actually writing a full-on charm. + +``` + +> See next: [`ops` | Manage libraries]() + + +(publish-a-library)= +## Publish a library on Charmhub + + +```{caution} + +On Charmhub, a library is always associated with the charm that it was first created for. When you publisht it to Charmhub, it's published to the page of that charm. To be able to publish it, you need to be logged in to Charmhub as a user who owns the charm (see more: {ref}`publish-a-charm`) or as a user who is registered as a contributor to the charm (a status that can be requested via [Discourse](https://discourse.charmhub.io/). + +``` + +To publish a library on Charmhub, in the root directory of the charm that holds the library, run `charmcraft publish-lib` followed by the full library path on the template `charms..v.`. For example: + +```text +$ charmcraft publish-lib charms.demo.v0.demo +Library charms.demo.v0.demo sent to the store with version 0.1 +``` + +> See more: {ref}`ref_commands_publish-lib` + +This will upload the library's content to Charmhub. + +To update the library on Charmhub, update the `LIBAPI`/`LIBPATCH` metadata fields inside the library file, then repeat the publish procedure. + +```{caution} **About the metadata fields:** + +Most times it is enough to just increment `LIBPATCH` but, if you're introducing breaking changes, you must work with the major API version. + +Additionally, be mindful of the fact that users of your library will update it automatically to the latest PATCH version with the same API version. To avoid breaking other people's library usage, make sure to increment the `LIBAPI` version but reset `LIBPATCH` to `0`. Also, before adding the breaking changes and updating these values, make sure to copy the library to the new path; this way you can maintain different major API versions independently, being able to update, for example, your v0 after publishing v1. See more: {ref}`file-libname-py`. +``` + +> See more: {ref}`ref_commands_publish-lib` + + +To share your library with other charm developers, navigate to the host charm's Charmhub page, go to Libraries tab, then copy and share the URL at the top of the page. + + +## View the libs published for a charm + +The easiest way to find an existing library for a given charm is via `charmcraft list-lib`, as shown below. This will query Charmhub and show which libraries are published for the specified charm, along with API/patch versions. + + jdoe@machine:/home/jane/autoblog$ charmcraft list-lib blogsystem + Library name API Patch + superlib 1 0 + +The listing will not show older API versions; this ensures that new users always start with the latest version. + +Another good way to search for libraries is to explore the charm collection on [Charmhub](https://charmhub.io/). + +> See more: {ref}``charmcraft list-lib` ` + + +## Use a library + +In your charm's `charmcraft.yaml`, specify the `charm-libs` key with the desired libraries. + +> See more: {ref}`file-charmcraft-yaml-charm-libs` + + +In your charm's root directory, run `charmcraft fetch-libs`. Charmcraft will download the libraries to your charm's directory. + +> See more: {ref}`ref_commands_fetch-libs` + + +To use a library in your `src/charm.py`, import it using its fully-qualified path minus the `lib` part: + +```python +import charms.demo.v0.demo +``` + +To update your lib with the latest published version, repeat the process. + + + diff --git a/docs/howto/manage-names.md b/docs/howto/manage-names.md new file mode 100644 index 000000000..916e17dc3 --- /dev/null +++ b/docs/howto/manage-names.md @@ -0,0 +1,36 @@ +(manage-names)= +# How to manage names + + +(register-a-name)= +## Register a name on Charmhub + + +To register a name for your charm on Charmhub, use the `chamrcraft register` command followed by your desired name. E.g., + +```bash +$ charmcraft register my-awesome-charm +Congrats! You are now the publisher of 'my-awesome-charm' +``` + +> See more: {ref}`ref_commands_register` + +This also automatically creates four channels, all with track `latest` but risk level `edge`, `beta`, `candidate`, and `stable`, respectively. + +> See more: {ref}`manage-channels` + +## View registered names + +To view the names you've registered on Charmhub, run `charmcraft names`. + +> See more: {ref}`ref_commands_names` + +## Unregister a name + +```{caution} +A name can be unregistered only if you haven't yet uploaded anything to it. +``` + +To unregister a name, run `charmcraft unregister` followed by the name. + +> See more: {ref}`ref_commands_unregister` diff --git a/docs/howto/manage-parts.md b/docs/howto/manage-parts.md new file mode 100644 index 000000000..f9da9c76b --- /dev/null +++ b/docs/howto/manage-parts.md @@ -0,0 +1,48 @@ +(manage-parts)= +# How to manage parts + +> See first: {ref}`part` + + +## Remove a part's assets + +TBA + +> See more: {ref}`ref_commands_clean` + + +## Download or retrieve artifacts defined for a part + +TBA + +> See more: {ref}`ref_commands_pull` + + +## Build artifacts defined for a part + +TBA + +> See more: {ref}`ref_commands_build` + + +## Stage built artifacts into a common staging area + +TBA + +> See more: {ref}`ref_commands_build` + + +## Prime artifacts defined for a part + +TBA + +> See more: {ref}`ref_commands_prime` + + +## Build the charm or bundle + + +TBA + +> See more: {ref}`ref_commands_pack` + diff --git a/docs/howto/manage-resources.md b/docs/howto/manage-resources.md new file mode 100644 index 000000000..b6edd18b8 --- /dev/null +++ b/docs/howto/manage-resources.md @@ -0,0 +1,97 @@ +manage-resources)= +# How to manage resources + +> See first: [`juju` | Charm resource](https://juju.is/docs/juju/charm-resource), [`juju` | Manage resources](https://juju.is/docs/juju/manage-charm-resources) + +## Declare a resource + +To declare a resource required by your charm, in your charm's `charmcraft.yaml file` specify the `resources` key. + +> See more: {ref}`file-charmcraft-yaml-resources` +> +> See next: [`ops` | Manage resources]() + + +````{tip} +During development, it may be useful to specify the resource at deploy time to facilitate faster testing without the need to publish a new charm/resource in between minor fixes. For example, assuming the resource is a `/tmp/somefile.txt` file, you could pack and the deploy with `juju deploy ... --resource`: + +```text +echo "TEST" > /tmp/somefile.txt +charmcraft pack +juju deploy ./my-charm.charm --resource my-resource=/tmp/somefile.txt +``` + +```` + +(publish-a-resource)= +## Publish a resource on Charmhub + +```{note} +You must have already published the charm. See more: {ref}`publish-a-charm`. + +``` + +To publish a resource on its charm's Charmhub page, run the `charmcraft upload-resource` command followed by the name of the charm, the name of the resource (cf. `charmcraft.yaml`), and `--filepath=` / `--image=`. For example: + +```{note} + +The option `--image` must indicate an OCI image's digest, being it in the short or long form (e.g.: `70aa8983ec5c` or `sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249`). When using the "short form" of the digest, the image needs to be present locally so its proper ID (the "long form") can be retrieved. + + +``` + + +```text +$ charmcraft upload-resource my-super-charm someresource --filepath=/tmp/superdb.bin +Revision 1 created of resource 'someresource' for charm 'my-super-charm' +``` + +```text +$ charmcraft upload-resource my-super-charm redis-image --image=sha256:64aa8983ec5cea7bc143af18829836914fa405184d56dcbdfd9df672ade85249 +Revision 1 created of resource 'redis-image' for charm 'my-super-charm' +``` + +Charmcraft will first check if that specific image is available in Canonical's Registry, and just use it if that's the case. If not, it will try to get it from the developer's local OCI repository (needs `dockerd` to be installed and running), push it to the Canonical's Registry, and then use it. Either way, when the upload has completed, you end up with a resource revision. + +To update a pre-uploaded resource, run the `upload-resource` command again. The result will be a new revision. + +> See more: {ref}`ref_commands_upload-resource` + +## View all the resources published on Charmhub + +To view all the resources published on Charmhub for a charm, run `charmcraft resources` followed by the charm name: + +```{important} + +**If you're not logged in to Charmhub:** The command will open up a browser window and ask you to log in. + +``` + +```text +$ charmcraft resources mycharm +``` + +> See more: {ref}`ref_commands_resources` + +(manage-resource-revisions)= +## Manage resource revisions +### List all the available resource revisions + +To view all the revisions for a resource associated with a charm you've uploaded to Charmhub, run `charmcraft resource-revisions` followed by the charm name and the resource name. For example: + +```text +$ charmcraft resource-revisions mycharm myresource +``` + +> See more: {ref}`ref_commands_resource-revisions` + +### Set the architectures for a resource revision + +To set the architectures for a revision of a resource associated with a charm you've uploaded to Charmhub, run `charmcraft set-resource-architectures` followed by the name of the charm, the name of the resource, and the architecture(s), using the `--resources` flag to specify the target resource revision. For example: + +```text +$ charmcraft set-resource-architectures mycharm myresource --revision=1 arm64,armhf +``` + +> See more: {ref}`ref_commands_set-resource-architectures` + diff --git a/docs/howto/manage-revisions.md b/docs/howto/manage-revisions.md new file mode 100644 index 000000000..83bcecc9a --- /dev/null +++ b/docs/howto/manage-revisions.md @@ -0,0 +1,81 @@ +(manage-charm-revisions)= +# How to manage charm revisions + + + +## Create a charm revision + +A charm revision is created implicitly every time you upload a charm to Charmhub (unless you're uploading the exact same file again). + +> See more: {ref}`publish-a-charm` + +## View the existing charm revisions + + + To inspect the existing charm revisions, run `charmcraft revisions` followed by the name of the charm. + +> See more: {ref}`ref_commands_revisions` + + +## Promote a charm revision to a better risk level + +To promote a charm revision to a higher-ranking risk level, + + + +use the GitHub `promote-charm` action. + +> See more: [GitHub | canonical/charming-actions/promote-charm](https://github.com/canonical/charming-actions/tree/2.6.0/promote-charm) + +````{dropdown} Example outcome + +For example, in the (partial) output of juju info mongodb below, revision 100 has been promoted from `3.6/edge` through `3.6/beta` and `3.6/candidate` all the way to `3.6/stable`. (The up arrow next to `3.6/beta` indicates that that channel has been closed and, if you try `juju deploy --channel 3.6/beta`, what you’ll get is the next higher-ranking risk level of the same track, that is, `3.6/candidate`.) + +```text +channels: | + 5/stable: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 + 5/candidate: 117 2023-04-20 (117) 12MB amd64 ubuntu@22.04 + 5/beta: ↑ + 5/edge: 118 2023-05-03 (118) 13MB amd64 ubuntu@22.04 + 3.6/stable: 100 2023-04-28 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + 3.6/candidate: 100 2023-04-13 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + 3.6/beta: ↑ + 3.6/edge: 100 2023-02-03 (100) 860kB amd64 ubuntu@20.04, ubuntu@18.04 + +``` +```` + + +(release-a-revision-into-a-channel)= +## Release a charm revision into a channel + +To release a specific charm revision to a channel, run `charmcraft release` followed by the name of the charm and flags specifying the revision and its target channel. E.g., + +```text +$ charmcraft release my-awesome-charm --revision=1 --channel=beta +Revision 1 of charm 'my-awesome-charm' released to beta +``` + +> See more: {ref}`ref_commands_release` + + +This opens the channel you're releasing to. + + +> See more: {ref}`manage-channels` + + +Following the release, Charmhub will display the charm's information at `charmhub.io/`. (The default information displayed is obtained from the most stable channel.) Your charm will also become available for download. + +> See more: [`juju` | Download a charm from Charmhub](https://juju.is/docs/juju/manage-charms-or-bundles#download-a-charmhub-charm) + + diff --git a/docs/howto/manage-the-charmcraft-cli.md b/docs/howto/manage-the-charmcraft-cli.md new file mode 100644 index 000000000..b4037f811 --- /dev/null +++ b/docs/howto/manage-the-charmcraft-cli.md @@ -0,0 +1,119 @@ +(manage-the-charmcraft-cli)= +# How to manage the `charmcraft` CLI + +> See first: {ref}`charmcraft-cli` + +## Install the `charmcraft` CLI + + +### On Linux + +The recommended way to install Charmcraft on Linux is from the `stable` channel via snap: + + sudo snap install charmcraft --classic + +There are multiple channels other than `stable`. See the full list with `snap info charmcraft`. + +We recommend either `latest/stable` or `latest/candidate` for everyday charming. With the snap you will always be up to date as Charmhub services and APIs evolve. Charmcraft supports Kubernetes operator development. + +In Linux, Charmcraft defaults to LXD to build the charms in a container matching the target base(s) (Multipass can also be used). Charmcraft will offer to install LXD if required, but here are steps to set it up manually: + +```text +$ sudo snap install lxd +$ sudo adduser $USER lxd +$ newgrp lxd +$ lxd init --auto +``` + +You can also install Charmcraft in an isolated environment. + +> See more: {ref}`install-in-an-isolated-environment` + +### On macOS + +Charmcraft is [available on homebrew](https://formulae.brew.sh/formula/charmcraft). + +Installation should be straightforward if using homebrew (if not already setup, refer to [this instructions](https://brew.sh/)). + +```text +$ brew install charmcraft +==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/manifests/1.3.2 +######################################################################## 100.0% +==> Downloading https://ghcr.io/v2/homebrew/core/charmcraft/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d96bbcfb72bdc +==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:ebe7aac3dcfa401762faaf339a28e64bb5fb277a7d +######################################################################## 100.0% +==> Pouring charmcraft--1.3.2.mojave.bottle.tar.gz +🍺 /usr/local/Cellar/charmcraft/1.3.2: 2,205 files, 17.2MB +``` + +Charmhub commands work natively: + +```text +$ charmcraft whoami +name: John Doe +username: jdoe +id: xxxxxxxxxxxxxxxxxxxxxxxxx +``` + +In macOS, Charmcraft defaults to Multipass to build the charms in a container matching the target base(s). Running pack asks to setup Multipass if not already installed, and continues with the packing process: + +```text +$ charmcraft pack +Multipass is required, but not installed. Do you wish to install Multipass and configure it with the defaults? [y/N]: y +==> Downloading https://github.com/canonical/multipass/releases/download/v1.7.2/multipass-1.7.2+mac-Darwin.pkg +Already downloaded: /Users/jdoe/Library/Caches/Homebrew/downloads/4237fcef800faa84459a2911c3818dfa76f1532d693b151438f1c8266318715b--multipass-1.7.2+mac-Darwin.pkg +==> Installing Cask multipass +==> Running installer for multipass; your password may be necessary. +Package installers may write to any location; options such as `--appdir` are ignored. +installer: Package name is multipass +installer: Installing at base path / +installer: The install was successful. +🍺 multipass was successfully installed! +Packing charm 'test-charm_ubuntu-20.04-amd64.charm'... +Starting charmcraft-test-charm-12886917363-0-0-amd64 ... +``` + +You can also install Charmcraft in an isolated environment. + +> See more: {ref}`install-in-an-isolated-environment` + + +### On Windows + +There is no previously packaged way to install Charmcraft in Windows, please refer to how to install it in an [isolated environment](#heading--isolated). + + +(install-in-an-isolated-environment)= +### In an isolated environment on Linux, macOS, or Windows + +One way to install Charmcraft is via [Multipass](https://multipass.run/). This is a good way to install it on any platform, as it will give you an isolated development environment. + +First, [install Multipass](https://multipass.run/docs/how-to-install-multipass). + +Second, use Multipass to provision a virtual machine. The following command will launch a fresh new VM with 4 cores, 8GB RAM and a 20GB disk and the name 'charm-dev': + +```text +$ multipass launch --cpus 4 --memory 8G --disk 20G --name charm-dev +``` + +Last, open a shell in your new Ubuntu virtual machine, and install Charmcraft there: + +```text +$ multipass shell charm-dev +... +ubuntu@charm-dev:~$ sudo snap install charmcraft --classic +charmcraft 2.2.0 from Canonical✓ installed +``` + +That's it. You can now start typing in Charmcraft commands. + + +## Check the installed version of the `charmcraft` CLI + +To check the installed version, run: + +```text +charmcraft version +``` + +> See more: {ref}`ref_commands_version` diff --git a/docs/howto/manage-the-current-charmhub-user.md b/docs/howto/manage-the-current-charmhub-user.md new file mode 100644 index 000000000..bb3f9c597 --- /dev/null +++ b/docs/howto/manage-the-current-charmhub-user.md @@ -0,0 +1,99 @@ +(manage-the-current-charmhub-user)= +# How to manage the current Charmhub user +> See first: [`juju` | Charmhub](), [Charmhub](https://charmhub.io/) + +## Log in to Charmhub + +### Local environments + +To log in to Charmhub, run `charmcraft login`: + +``` +$ charmcraft login +Opening an authorization web page in your browser. +If it does not open, please open this URL: +... +``` + +> See more: {ref}`ref_commands_login` + + +### Remote environments + +> Introduced in Charmcraft 1.3 + +When working locally with Charmcraft, the developer will use `charmcraft login` to get authentication tokens from Charmhub, which would be stored in the local keyring and used on all operations that need them. + +This is fine for local environments, but for remote ones (e.g. a CI/CD system) on one hand it's desirable to not login using the standard method (a browser opening an authentication web page to insert user, password, and *2FA*), and on the other hand the authentication tokens should be limited in different ways. + +The alternative login method is implemented through the `CHARMCRAFT_AUTH` environment variable, which needs to be set to useful credentials (which are obtained using the `--export` option, see below). + +If that variable is set Charmcraft will use it for all operations that needs authentication against Charmhub. Note that in this case the `login` and `logout` commands can not be used. + +To obtain credentials to be used in `CHARMCRAFT_AUTH`, the `login` command has the `--export` option, which accepts a file path. If specified, it will override the regular behaviour of storing the credentials in the user's keyring, and those will be exported to the given file path. The content of this file is what should be used verbatim in the environment variable. + +As mentioned at the beginning, it's also a good idea to use restricted credentials in a remote system. For this situation, the Charmcraft's `login` command provides different options to attenuate the obtained authentication tokens: + +- `--charm`: the charm name on which the permission will apply (can be specified multiple times) +- `--bundle`: the bundle name on which the permission will apply (can be specified multiple times) +- `--permission`: what action can be done on the specified package(s) (see below for a list; can be specified multiple times) +- `--channel`: the channel on which the package can be released (can be specified multiple times) +- `--ttl`: the time, in seconds, that the granted token will be useful (defaults to 30 days) + +All these indications are optional, and default to no restrictions applied on each category (except indicated the time-to-live, as indicated above). Note also that these restrictions can only be used if the credentials are exported to a file with the `--export` option. + +The available permissions are: +- `account-register-package`: register/request a new package name +- `account-view-packages`: list packages owned by the account or for which this account has collaborator rights +- `package-manage`: meta permission that includes all the `package-manage-*` ones +- `package-manage-acl`: add/invite/remove collaborators +- `package-manage-metadata`: edit metadata, add/remove media, etc. +- `package-manage-releases`: release revisions and close channels +- `package-manage-revisions`: upload new blobs, check for upload status +- `package-view`: meta permission that includes all the `package-view-*` ones +- `package-view-acl`: list the collaborators for a package, return privacy settings +- `package-view-metadata`: view the metadata for a package, including media +- `package-view-metrics`: view the metrics of a package +- `package-view-releases`: list the current releases (channel map) and the history of releases for a package +- `package-view-revisions`: list the existing revisions for a package, along with status information + +So, an example sequence for requesting/using credentials to set up a CI/CD system that will push and release a charm could be: + +- get the specific credentials in a file: + ```bash + $ charmcraft login --export=secrets.auth --charm=my-super-charm --permission=package-manage --channel=edge --ttl=2592000 + Login successful. Credentials exported to 'secrets.auth'. + ``` +- test that all is fine; for this get the content: + ```bash + $ CHARMCRAFT_AUTH=`cat test1` charmcraft whoami + name: J. Doe + username: jdoe-superdev + id: VTLZAToLcdaIPtisVBjfiQYCXbpKwbCc + charms: + - my-super-charm + permissions: + - package-manage + channels: + - edge + time to live (s): 2592000 + ``` +- to use this authorization token on a CI/CD system set the environment variable CHARMCRAFT_AUTH with the content of `secrets.auth` file, and use Charmcraft as normal: + + ```bash + export CHARMCRAFT_AUTH= + ... + charmcraft upload my-super-charm.charm --release edge + ``` + +## Check currently logged in user + +To check the currently logged in user, run `charmcraft whoami`. + +> See more: {ref}`ref_commands_whoami` + +## Log out of Charmhub + +To log out of Charmhub, run `charmcraft logout`. + +> See more: {ref}`ref_commands_logout` diff --git a/docs/howto/manage-tracks.md b/docs/howto/manage-tracks.md new file mode 100644 index 000000000..d26b910a0 --- /dev/null +++ b/docs/howto/manage-tracks.md @@ -0,0 +1,101 @@ +(manage-tracks)= +# How to manage tracks + +> See also: {ref}`track` + +When you register a charm name on Charmhub, you automatically get 4 channels, all with track `latest`. However, as your charm evolves, you'll likely want to customise the shape of this track (e.g., to align with the workload) and then create new tracks in the new pattern. This document shows you how. + +(request-a-track-guardrail)= +## Request a track guardrail +> See also: {ref}`guardrail` + +To request a track guardrail, contact a Charmhub admin by creating a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . + + +(create-a-track)= +## Create a track + +Once you've requested a track guardrail, there are two ways to create a new track for your charm -- you can keep contacting a Charmhub admin every time or you can self-service. For most cases the latter option is likely to be more convenient and faster. + +### Ask a Charmhub admin + +To create a new track by contacting a Charmhub admin, create a post on Discourse under the **charmhub requests** category, that is, here: https://discourse.charmhub.io/c/charmhub-requests/46 . The admin will create the new track that fits within the track guardrail you’ve set up for your charm. + +### Create it yourself + +To create a new track yourself, follow the steps below: + +```{important} + +As you might notice, this path is currently a little hacky. In the long-term it should become a lot smoother as there are plans to support it through the Charmcraft CLI. + +``` + +```{important} + +As you will see, this method currently relies on `charmcraft`+ `curl`. We recommend the Charmcraft bit because Charmcraft already understands the authentication mechanism used by Charmhub and can generate a suitable authentication token (macaroon) that will make it possible to then use `curl` directly to interact with the Charmhub API. This method also has the advantage that it can be adapted to use any HTTP client or library as long as it can pass custom headers. + +``` + + +**1. Enable `curl` access to the Charmhub API.** + +First, install `curl` and `jq`. + +```{important} + +You might already have both. + +``` + +Then, use Charmcraft to log in to Charmhub and export your Charmhub credentials / token (macaroon) to a file: + +```text +charmcraft login --export charmhub-creds.dat +``` + +Next, decode and extract the macaroon from the .dat file and place it in a header in an environment variable: + +```text +export CHARMHUB_MACAROON_HEADER="Authorization: Macaroon $(cat charmhub-creds.dat | base64 -d | jq -r .v)" +``` + +At this point you can use this variable in `curl` commands -- just make sure to specify the correct `Content-Type`. + +**2. Use `curl` to view the existing guardrails and tracks.** To view the guardrails and tracks associated with your charm, issue an HTTP `GET` request to `/v1//`. For example, for a charm named `hello-world-charm`: + +```text +curl https://api.charmhub.io/v1/charm/hello-world-charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" +``` + +The guardrails and tracks of the package will be under the `track-guardrails` and `tracks` keys of `metadata`. Now you know what the new track may look like. + +> See more: [Charmhub API docs > `package_metadata`](https://api.charmhub.io/docs/default.html#package_metadata) + +````{important} + +**If you want to view the guardrails and tracks for *all* published charms:** Issue an HTTP `GET` request to `/v1/`, as below: + +```text +curl https://api.charmhub.io/v1/charm -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" +``` + +> See more: [Charmhub API docs > `list_registered_names`](https://api.charmhub.io/docs/default.html#list_registered_names) + + +```` + + +**3. Use `curl` to create a new track.** Finally, to create a new track for your charm, issue an HTTP `POST` request to `/v1///tracks`, where `name` and `namespace` refer to the name and type of the package respectively. For example, given a charm named `hello-world-charm`, one can create two tracks `v.1` and `v.2` as follows: + +```text +curl https://api.charmhub.io/v1/charm/hello-world-charm/tracks -X POST -H'Content-type: application/json' -H "$CHARMHUB_MACAROON_HEADER" -d '[{"name": "v.1"}, {"name": "v.2"}]' +``` + +Of course, the tracks must conform to the existing guardrail for the charm. + + +> See more: [Charmhub API docs > `create_tracks`](https://api.charmhub.io/docs/default.html#create_tracks) + + +That's it, you now have a new track for your charm! diff --git a/docs/howto/shared-cache.rst b/docs/howto/misc/#shared-cache.rst# similarity index 100% rename from docs/howto/shared-cache.rst rename to docs/howto/misc/#shared-cache.rst# diff --git a/docs/howto/charm-to-poetry.rst b/docs/howto/misc/charm-to-poetry.rst similarity index 100% rename from docs/howto/charm-to-poetry.rst rename to docs/howto/misc/charm-to-poetry.rst diff --git a/docs/howto/charm-to-python.rst b/docs/howto/misc/charm-to-python.rst similarity index 100% rename from docs/howto/charm-to-python.rst rename to docs/howto/misc/charm-to-python.rst diff --git a/docs/howto/misc/index.md b/docs/howto/misc/index.md new file mode 100644 index 000000000..37fd765c0 --- /dev/null +++ b/docs/howto/misc/index.md @@ -0,0 +1,16 @@ +(howto-misc)= +# Miscellaneous + +```{toctree} +:maxdepth: 2 + +Migrate to `poetry` +Migrate to `python` +Cache intermediate build artefacts +Pack a hook-based charm with `charmcraft` +Pack a reactive-based charm with `charmcraft` +``` + + diff --git a/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md new file mode 100644 index 000000000..82cd53096 --- /dev/null +++ b/docs/howto/misc/pack-a-hooks-based-charm-with-charmcraft.md @@ -0,0 +1,158 @@ +(pack-a-hooks-based-charm-with-charmcraft.md)= +# How to pack a hooks-based charm with Charmcraft + +> See also: +> - {ref}`How to set up a charm project ` +> - {ref}`About charm types, by creation type ` +> - {ref}`How to turn a hooks-based charm into an ops charm ` + +You want a hooks-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. + +```{note} + + Introduced in Charmcraft 1.4 + +``` + + + + +Suppose you have a hooks-only charm, for example, [tiny-bash](https://github.com/erik78se/tiny-bash), which you can obtain as follows: + + +```text +$ git clone https://github.com/erik78se/tiny-bash +``` + + + +To make it packable by Charmcraft, all you need to do is navigate inside the charm directory and create a `charmcraft.yaml` file with the part definition for a hooks-based charm, as shown below: + + +```yaml +type: charm + +bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" + +parts: + tiny-bash: + plugin: dump + source: . + prime: + - LICENSE + - README.md + - config.yaml + - copyright + - hooks + - icon.svg + - metadata.yaml +``` + + + +After this, you can pack your charm with `charmcraft pack`, as usual: + +```text +$ charmcraft pack +Charms packed: + tiny-bash_ubuntu-20.04-amd64.charm +``` + +If successful, the result should look as below, i.e., the charm file should contain all the files listed in the `prime` section of the `tiny-bash` part and the charm manifest. + +```shell +$ unzip -l tiny-bash_ubuntu-20.04-amd64.charm +Archive: tiny-bash_ubuntu-20.04-amd64.charm + Length Date Time Name +--------- ---------- ----- ---- + 423 2021-11-12 19:37 metadata.yaml + 431 2021-11-12 19:37 README.md + 12 2021-11-12 19:37 config.yaml + 3693 2021-11-12 19:37 icon.svg + 38 2021-11-12 19:37 copyright + 261 2021-11-12 20:08 manifest.yaml + 34523 2021-11-12 19:37 LICENSE + 381 2021-11-12 19:37 hooks/update-status + 346 2021-11-12 19:37 hooks/start + 1294 2021-11-12 19:37 hooks/shared-fs-relation-changed + 563 2021-11-12 19:37 hooks/stop + 497 2021-11-12 19:37 hooks/leader-elected + 447 2021-11-12 19:37 hooks/install + 417 2021-11-12 19:37 hooks/leader-settings-changed + 811 2021-11-12 19:37 hooks/upgrade-charm + 625 2021-11-12 19:37 hooks/config-changed +--------- ------- + 44762 16 files +``` + + + +And you can also deploy your application with `juju deploy`, as usual: + +```shell +$ juju deploy ./tiny-bash_ubuntu-20.04-amd64.charm +Located local charm "tiny-bash", revision 0 +Deploying "tiny-bash" from local charm "tiny-bash", revision 0 +``` +If successful, the result should look as below, i.e., with the application status active. + +```text +$ juju status +Model Controller Cloud/Region Version SLA Timestamp +default localhost-localhost localhost/localhost 2.9.12 unsupported 17:23:23-03:00 + +App Version Status Scale Charm Store Channel Rev OS Message +tiny-bash active 1 tiny-bash local 0 ubuntu update-status ran: 20:22 + +Unit Workload Agent Machine Public address Ports Message +tiny-bash/0* active idle 0 10.2.17.31 update-status ran: 20:22 + +Machine State DNS Inst id Series AZ Message +0 started 10.2.17.31 juju-55481c-0 focal Running +``` + + + + diff --git a/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md new file mode 100644 index 000000000..29b5a0071 --- /dev/null +++ b/docs/howto/misc/pack-a-reactive-based-charm-with-charmcraft.md @@ -0,0 +1,126 @@ +(pack-a-reactive-based-charm-with-charmcraft)= +# How to pack a reactive-based charm with Charmcraft + +> See also: +> - {ref}`How to set up a charm project ` +> - {ref}`How to pack your charm using Charmcraft ` +> - {ref}`About charm types, by creation type ` + + +Suppose you want a reactive-based charm. Such a charm cannot be initialised with Charmcraft. However, it can be *packed* with Charmcraft. This document shows you how. + +```{note} + + Introduced in Charmcraft 1.4. + +``` +```{note} + +The reactive way to write a charm represents an old standard. The recommended way to create a charm now is using {ref}`Charmcraft ` and {ref}`Ops `. + +``` + +To pack a reactive-based charm with Charmcraft, in the charm directory create a `charmcraft.yaml` file with the part definition for a reactive-based charm: + +```yaml +type: "charm" +bases: + - build-on: + - name: "ubuntu" + channel: "20.04" + run-on: + - name: "ubuntu" + channel: "20.04" +parts: + charm: + source: . + plugin: reactive + build-snaps: [charm] +``` + +Done. Now you can go ahead and pack your reactive-based charm with Charmcraft in the usual way using `charmcraft pack`. + + diff --git a/docs/howto/misc/shared-cache.rst b/docs/howto/misc/shared-cache.rst new file mode 100644 index 000000000..9fe793c68 --- /dev/null +++ b/docs/howto/misc/shared-cache.rst @@ -0,0 +1,78 @@ +.. _howto-shared-cache: + +Cache intermediate build artefacts +================================== + +Because Charmcraft builds Python packages from source rather than using pre-built +wheels, the initial builds of charms can take a while. The intermediate artefacts +get cached, which significantly speeds up subsequent builds. + +When installed as a snap, Charmcraft automatically caches these wheels in the +``~/snap/charmcraft/common/cache`` directory. However, in some cases, it may be +beneficial to change this directory. + +This can be especially useful in CI, where you may wish to specify a directory that +gets cached between CI runs. + +Local usage +----------- + +When packing locally, you can change where Charmcraft caches build artefacts by setting +the ``CRAFT_SHARED_CACHE`` environment variable to the path of an existing directory to +use instead:: + + mkdir -p /tmp/charmcraft + CRAFT_SHARED_CACHE=/tmp/charmcraft charmcraft pack + +On GitHub +--------- + +While it's recommended that you use the ``charmcraft/pack`` action from +`craft-actions`_ where possible, the following workflow will manually pack a charm, +caching the intermediate files: + +.. code-block:: yaml + + name: Pack charm + on: + pull_request: + jobs: + pack: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: canonical/craft-actions/charmcraft/setup + - uses: actions/cache@v4 + with: + path: ${{ runner.temp }} + key: charmcraft-cache-${{ hashfiles('requirements.txt') }} + restore-keys: | + charmcraft-cache- + - env: + CRAFT_SHARED_CACHE: ${{ runner.temp } + run: | + charmcraft pack + +On GitLab +--------- + +The following example ``gitlab-ci.yml`` will install and run Charmcraft to pack your +charm, caching the intermediate artefacts: + +.. code-block:: yaml + + pack-charm: + cache: + - key: + files: + - requirements.txt + paths: + - .charmcraft_cache/ + variables: + CRAFT_SHARED_CACHE: .charmcraft_cache/ + script: + - mkdir -p .charmcraft_cache + - snap install charmcraft + - charmcraft pack + +.. _craft-actions: https://github.com/canonical/craft-actions diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..5d41194ce --- /dev/null +++ b/docs/index.md @@ -0,0 +1,81 @@ +# Charmcraft (`charmcraft`) + +```{toctree} +:maxdepth: 2 +:hidden: true + +tutorial/index +howto/index +reference/index +explanation/index +``` + +Charmcraft (`charmcraft`) is a tool designed to simplify the creation, building, and sharing of a [`juju` charm](https://juju.is/docs/juju/charmed-operator). + +When you initialise a charm with the `charmcraft` CLI, you automatically get all the crucial project files, pre-populated with helpful template content. These files are such that they can be `charmcraft`-packed right away; however, to make them meaningul for the application you are charming, you'll want to customise the YAML and [`ops`](https://juju.is/docs/sdk/ops)-powered Python in these files. For certain types of applications (Django, FastAPI, Flask, Go), if you initialise with a suitable `charmcraft` extension, things are even easier -- just tweak a few values in the YAML and you get a fully functioning charm. Either way, once you're pleased with what you've got, you can again use `chamcraft` to publish your charm on [Charmhub](https://charmhub.io/). + +You can create, build, and share a charm any way you want, but with `charmcraft` you get state-of-the-art results in record time. + +If you're a charm author, you *must* use `charmcraft`! + + +--------- + +## In this documentation + +````{grid} 1 1 2 2 + +```{grid-item-card} [Tutorial](/index) +:link: tutorial/index +:link-type: doc + +**Start here**: a hands-on introduction to Example Product for new users +``` + +```{grid-item-card} [How-to guides](/index) +:link: howto/index +:link-type: doc + +**Step-by-step guides** covering key operations and common tasks +``` + +```` + +````{grid} 1 1 2 2 +:reverse: + +```{grid-item-card} [Reference](/index) +:link: reference/index +:link-type: doc + +**Technical information** - specifications, APIs, architecture +``` + +```{grid-item-card} [Explanation](/index) +:link: explanation/index +:link-type: doc + +**Discussion and clarification** of key topics +``` + +```` + +--------- + + +## Project and community + +Charmcraft is a member of the Canonical family. It's an open source project +that warmly welcomes community projects, contributions, suggestions, fixes +and constructive feedback. + +* [Ubuntu Code of Conduct](https://ubuntu.com/community/code-of-conduct). +* [Canonical contributor licenses agreement](https://ubuntu.com/legal/contributors). + + diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 46bf6025c..000000000 --- a/docs/index.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. Charmcraft documentation root file - -Charmcraft -========== - -Charmcraft is part of the `Juju Charm SDK `_. -Most of Charmcraft's documentation is available there. - -.. toctree:: - :maxdepth: 1 - :hidden: - - tutorial/index - howto/index - reference/index - explanation/index - -.. grid:: 1 1 2 2 - - .. grid-item-card:: `Tutorial `_ - - **Get started** with a hands-on introduction to Charmcraft - - .. grid-item-card:: :ref:`How-to guides ` - - **Step-by-step guides** covering key operations and common tasks - -.. grid:: 1 1 2 2 - :reverse: - - .. grid-item-card:: :ref:`Reference ` - - **Technical information** about Charmcraft - - .. grid-item-card:: :ref:`Explanation ` - - **Discussion and clarification** of key topics - -Project and community -===================== - -Charmcraft is a member of the Canonical family. It's an open source project -that warmly welcomes community projects, contributions, suggestions, fixes -and constructive feedback. - -* `Ubuntu Code of Conduct `_. -* `Canonical contributor licenses agreement - `_. - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` diff --git a/docs/reference/charmcraft-analyzers-and-linters.md b/docs/reference/charmcraft-analyzers-and-linters.md new file mode 100644 index 000000000..ab12eba10 --- /dev/null +++ b/docs/reference/charmcraft-analyzers-and-linters.md @@ -0,0 +1,88 @@ +(charmcraft-analyzers-and-linters)= +# `charmcraft` analyzers & linters + +The following are the different checks that Charmcraft will run explicitly (when the user executes its `analyse` method) or implicitly (when packing charms). + +Any linter or analysis can be set in [the config](https://juju.is/docs/sdk/charmcraft-config) to be excluded from the normal execution. Also note that if any linter ends in error it will block the charm packing (you can pack it anyway using the `--force` option). + +You can read more about these checks in the [Charmcraft Analyze Specification](https://discourse.charmhub.io/t/proposal-charmcraft-analyze/4792). + + +## Language attribute + +If through analysis, the charm can be detected as being a Python based charm, then language shall be set to `python`. If not, it shall be set to `unknown`. + +When working with Python, it is possible to only publish byte-code. By doing so, troubleshooting is a harder task. Charms with Python sources delivered are preferred. + +This attribute meets the requirements to be set to `python` when: + +- the charm has a text dispatch which executes a .py +- the charm has a .py entry point +- the entry point file is executable + +## Framework attribute + + +When using `ops`, it is best to import it from a common path and not make customisation or package forks from it. If `ops` code is detected in the charm sources, this attribute's value shall be set to `operator`. If not, the charm may be using the Reactive Framework, and in this case the attribute value will be `reactive`. Else, it shall be set to `unknown`. + +This check hint meets the requirements for `ops` when: + +- language attribute is set to `python` +- the charm contains `venv/ops` +- the charm imports `ops` in the entry point + +The requirements for the Reactive Framework are: + +...or the Reactive Framework is used, if the charm... + +- has a metadata.yaml with `name` in it +- has a `reactive/.py` file that imports `charms.reactive` +- has a file name that starts with `charms.reactive-` inside the `wheelhouse` directory + + +## Juju metadata linter + +This linter verifies that the `metadata.yaml` file exists and is sane. + +The charm is considered to have a valid metadata if the following checks are true: + +- the `metadata.yaml` is present +- it is a valid YAML file +- it has at least the following fields: `name`, `summary`, and `description` + + +## Juju actions linter + +*(new in 1.4)* + +This linter verifies that the `actions.yaml` file, if exists, is a valid YAML file. The file is optional. The file contents are not verified. + + +## Juju config linter + +*(new in 1.4)* + +This linter verifies that the `config.yaml` file, if exists, is valid. This file is optional. + +If the file exists, it is considered valid if the following checks are true: + +- it has the `options` key +- it is a dictionary +- each item inside has the mandatory `type` key + +Check how to [create config.yaml and configure charms](https://discourse.charmhub.io/t/creating-config-yaml-and-configuring-charms/1039) for more information. + + +## Charm entrypoint linter + +*(new in 2.1)* + +Check the entry point is correct. Note that even if most modern charms has a typical `src/charm.py` entry point, not all charms have one, as Juju has different ways to deliver its events. + +This linter validates that, if an entry point is called from the `dispatch` file, that entry point... + +- exists +- is a file +- is executable + +The entry point content is *not* validated. diff --git a/docs/reference/charmcraft-cli.md b/docs/reference/charmcraft-cli.md new file mode 100644 index 000000000..a5f6a3605 --- /dev/null +++ b/docs/reference/charmcraft-cli.md @@ -0,0 +1,7 @@ +(charmcraft-cli)= +# `charmcraft` CLI + +```{toctree} +:maxdepth:2 +list-of-charmcraft-cli-commands +``` diff --git a/docs/reference/charmcraft.md b/docs/reference/charmcraft.md new file mode 100644 index 000000000..2f91ac28c --- /dev/null +++ b/docs/reference/charmcraft.md @@ -0,0 +1,7 @@ +(charmcraft)= +# `charmcraft` + +```{toctree} +:maxdepth: 2 +changelog +``` diff --git a/docs/reference/extension.md b/docs/reference/extension.md new file mode 100644 index 000000000..1f45b5f17 --- /dev/null +++ b/docs/reference/extension.md @@ -0,0 +1,15 @@ +(extension)= +# Extension + +> See also: {ref}`How to manage extensions ` + +In the context of building a charm, in Rockcraft and Charmcraft, an **extension** is a name you can pass to the `extensions` key of a rock's `rockcraft.yaml` file / a charm's `charmcraft.yaml` file that will include the usual keys, however, customised for a particular purpose. + +An extension is usually associated with a particular {ref}`profile `. + + +```{toctree} +:maxdepth: 2 + +extensions/index +``` diff --git a/docs/reference/extensions/charmcraft-extension-django-framework.md b/docs/reference/extensions/charmcraft-extension-django-framework.md new file mode 100644 index 000000000..3574fdb58 --- /dev/null +++ b/docs/reference/extensions/charmcraft-extension-django-framework.md @@ -0,0 +1,225 @@ +(charmcraft-extension-django-framework)= +# Charmcraft extension 'django-framework' + +The `django-framework` Charmcraft {ref}`extension ` includes configuration options customised for a Django application. This document describes all the keys that a user may interact with. + +```{tip} + +**If you'd like to see the full contents contributed by this extension:**
See {ref}`How to manage extensions `. + +``` + +## Database requirement + +Django requires a database to function. When generating a new project, the default is to make use of [SQLite^](https://www.sqlite.org/). Using SQLite is not recommended for production, especially on Kubernetes deployments, because the database is not shared across units and any contents will be removed upon a new container being deployed. The `django-framework` extension therefore requires a database integration for every application, such as [PostgreSQL^](https://www.postgresql.org/) or [MySQL^](https://www.mysql.com/). See {ref}`the how-to guide ` for how to deploy a database and integrate the Django application with it. + +## `charmcraft.yaml` > `config` > `options` + +You can use the predefined options (run `charmcraft expand-extensions` for details) but also add your own, as needed. + +In the latter case, any option you define will be used to generate environment variables; a user-defined option `config-option-name` will generate an environment variable named `DJANGO_CONFIG_OPTION_NAME` where the option name is converted to upper case, dashes will be converted to underscores and the `DJANGO_` prefix will be added. + +In either case, you will be able to set it in the usual way by running `juju config