Charm architecture

Web app support in Charmcraft and Rockcraft is a framework to easily deploy and operate your Flask, Django, FastAPI or Go workloads and associated infrastructure, such as databases and ingress, using open source tooling.

The resulting charm design leverages the sidecar pattern to allow multiple containers in each pod with Pebble running as the workload container’s entrypoint.

Pebble is a lightweight, API-driven process supervisor that is responsible for configuring processes to run in a container and controlling those processes throughout the workload lifecycle.

Pebble services are configured through layers, and the following containers represent each one a layer forming the effective Pebble configuration, or plan:

  1. An app container, which contains the workload to run in any of the supported web frameworks.

As a result, if you run a kubectl get pods on a namespace named for the Juju model you’ve deployed the web app charm into, you’ll see something like the following:

NAME                          READY   STATUS    RESTARTS   AGE
web-app-0                     2/2     Running   0          6h4m

This shows there are 2 containers - the named above, as well as a container for the charm code itself.

And if you run kubectl describe pod web-app-0, all the containers will have as Command /charm/bin/pebble. That’s because Pebble is responsible for the processes startup as explained above.

Charm architecture diagram

        C4Container
System_Boundary(web_app_charm, "Web app charm") {
   Container_Boundary(charm_container, "Charm Container") {
      Component(charm_logic, "Charm Logic", "Juju Operator Framework", "Controls application deployment & config")
   }
   Container_Boundary(web_app_container, "Workload Container") {
      Component(workload, "Workload", "Web Application", "Serves web requests")
   }
}
Rel(charm_logic, workload, "Supervises<br>process")
    

OCI images

We use Rockcraft to build OCI Images for the web app charm.

Metrics

The provided support for metrics and tracing depends on the enabled extension.

Integrations

The available integrations, including those that are already pre-populated, varies between extensions.

Juju events

For a web app charm, the following events are observed:

  1. <container name>_pebble_ready: fired on Kubernetes charms when the requested container is ready. Action: validate the charm configuration, run pending migrations and restart the workload.

  2. config_changed: usually fired in response to a configuration change using the CLI. Action: validate the charm configuration, run pending migrations and restart the workload.

  3. secret_storage_relation_created: fired when the relation is first created. Action: generate a new secret and store it in the relation data.

  4. secret_storage_relation_changed: fired when a new unit joins in an existing relation and whenever the related unit changes its settings. Action: validate the charm configuration, run pending migrations and restart the workload.

  5. secret_storage_relation_departed: fired when a unit departs from an existing relation. Action: validate the charm configuration, run pending migrations and restart the workload.

  6. update_status: fired at regular intervals. Action: validate the configuration, run pending migrations and restart the workload.

  7. secret_changed: fired when the secret owner publishes a new secret revision. Action: validate the configuration, run pending migrations and restart the workload.

  8. database_created: fired when a new database is created. Action: validate the charm configuration, run pending migrations and restart the workload.

  9. endpoints_changed: fired when the database endpoints change. Action: validate the charm configuration, run pending migrations and restart the workload.

  10. database_relation_broken: fired when a unit participating in a non-peer relation is removed. Action: validate the charm configuration, run pending migrations and restart the workload.

  11. ingress_ready: fired when the ingress for the web app is ready. Action: validate the charm configuration, run pending migrations and restart the workload.

  12. ingress_revoked: fired when the ingress for the web app is not ready anymore. Action: validate the charm configuration, run pending migrations and restart the workload.

  13. redis_relation_updated: fired when a new unit joins in an existing relation and whenever the related unit changes its settings. Action: validate the charm configuration, run pending migrations and restart the workload.

  14. s3_credentials_changed: fired when the S3 credentials are changed. Action: validate the charm configuration, run pending migrations and restart the workload.

  15. s3_credentials_gone: fired when the S3 credentials are removed. Action: validate the charm configuration, run pending migrations and restart the workload.

  16. saml_data_available: fired when new SAML data is present in the relation. Action: validate the charm configuration, run pending migrations and restart the workload.

  17. rabbitmq_ready: fired after a rabbitmq_cjoined event. Action: validate the charm configuration, run pending migrations and restart the workload.

  18. rabbitmq_connected: fired after a rabbitmq_changed or rabbitmq_broken event. Action: validate the charm configuration, run pending migrations and restart the workload.

  19. rabbitmq_joined: fired when a new unit joins in an existing relation. Action: request access to the RabbitMQ server and emit a connected event.

  20. rabbitmq_changed: fired when a new unit joins in an existing relation and whenever the related unit changes its settings. Action: request access to the RabbitMQ server and emit a ready event.

  21. rabbitmq_broken: fired when a unit participating in a non-peer relation is removed. Action: emit a ready event.

  22. rabbitmq_departed: fired when a related unit is no longer related. Action: validate the charm configuration, run pending migrations and restart the workload.

  23. tracing_endpoint_changed: fired when one of the receiver endpoints changes. Action: validate the charm configuration, run pending migrations and restart the workload.

  24. tracing_endpoint_removed: fired when one of the receiver endpoints is removed. Action: validate the charm configuration, run pending migrations and restart the workload.

  25. smtp_data_available: fired when new SMTP data is present in the relation. Action: validate the charm configuration, run pending migrations and restart the workload.

  26. rotate_secret_key: fired when secret-rotate is executed. Action: generate a new secret token for the application.

Charm code overview

The src/paas_charm/charm.py contains the charm logic that all supported frameworks will inherit and extend. Each framework will define its entry point in its own charm.py file, defining a class that will extend from PaasCharm.

PaasCharm is the base class from which all Charms are formed, defined by Ops (Python framework for developing charms).

See also

Charm

The __init__ method guarantees that the charm observes all events relevant to its operation and handles them.

Take, for example, when a configuration is changed by using the CLI.

  1. User runs the command

    juju config sample_config=sample_value
    
  2. A config-changed event is emitted.

  3. In the __init__ method is defined how to handle this event like this:

    self.framework.observe(self.on.config_changed, self._on_config_changed)
    
  4. The method _on_config_changed, for its turn, will take the necessary actions such as waiting for all the relations to be ready and then configuring the container.