Expose operational tasks via actions

From Zero to Hero: Write your first Kubernetes charm > Expose operational tasks via actions

See previous: Preserve your charm’s data

This document is part of a series, and we recommend you follow it in sequence. However, you can also jump straight in by checking out the code from the previous branches:

git clone https://github.com/canonical/juju-sdk-tutorial-k8s.git
cd juju-sdk-tutorial-k8s
git checkout 05_preserve_charm_data
git checkout -b  06_create_actions 

A charm should ideally cover all the complex operational logic within the code, to help avoid the need for manual human intervention.

Unfortunately, that is not always possible. As a charm developer, it is thus useful to know that you can also expose charm operational tasks to the charm user by defining special methods called actions.

This can be done by adding actions in a file called actions.yaml and then adding action event handlers to the src/charm.py file.

In this part of the tutorial we will follow this process to add an action that will allow a charm user to view the current database access points and, if set, also the username and the password.

Contents:

  1. Define the actions
  2. Define the action event handlers
  3. Validate your charm
  4. Review the final code

Define the actions

In the project root folder, create a file called actions.yaml.

Open the file and add to it a block defining an action, as below. As you can see, the action is called get-db-info and it is intended to help the user access database authentication information. The action has a single parameter, show-password; if set to True, it will show the username and the password.

get-db-info:
  description: Fetches Database authentication information
  params:
    show-password:
      description: "Show username and password in output information"
      type: boolean
      default: False

Read more: File ‘actions.yaml’, How to handle actions

Define the action event handlers

Open the src/charm.py file.

In the charm __init__ method, add an action event observer, as below. As you can see, the name of the event consists of the name defined in the actions.yaml file (get-db-info) and the word action.

# events on custom actions that are run via 'juju run-action'
self.framework.observe(self.on.get_db_info_action, self._on_get_db_info_action)

Read more: Event <action name>-action

Now, define the action event handler, as below: First, read the value of the parameter defined in the actions.yaml file (show-password). Then, use the fetch_postgres_relation_data method (that we defined in a previous chapter) to read the contents of the database relation data and, if the parameter value read earlier is True, add the username and password to the output. Finally, use event.set_results to attach the results to the event that has called the action; this will print the output to the terminal.

def _on_get_db_info_action(self, event) -> None:
    show_password = event.params["show-password"]  # see actions.yaml
    try:
        db_data = self.fetch_postgres_relation_data()
    except DatabaseNotReady:
        output = {"result": "No database connected"}
    else:
        output = {
            "db-host": db_data.get("db_host", None),
            "db-port": db_data.get("db_port", None),
        }

        if show_password:
            output.update(
                {
                    "db-username": db_data.get("db_username", None),
                    "db-password": db_data.get("db_password", None),
                }
            )
    event.set_results(output)

Validate your charm

First, repack and refresh your charm:

charmcraft pack
juju refresh \
  --path="./demo-api-charm_ubuntu-22.04-amd64.charm" \
  demo-api-charm --force-units --resource \
  demo-server-image=ghcr.io/canonical/api_demo_server:1.0.0

Next, test that the basic action invocation works:

juju run demo-api-charm/0 get-db-info --wait 1m

It might take a few seconds, but soon you should see an output similar to the one below, showing the database host and port:

Running operation 13 with 1 task
  - task 14 on unit-demo-api-charm-0

Waiting for task 14...
db-host: postgresql-k8s-primary.model2.svc.cluster.local
db-port: "5432"

Now, test that the action parameter (show-password) works as well by setting it to True:

juju run demo-api-charm/0 get-db-info show-password=True --wait 1m

The output should now include the username and the password:

Running operation 15 with 1 task
  - task 16 on unit-demo-api-charm-0

Waiting for task 16...
db-host: postgresql-k8s-primary.model2.svc.cluster.local
db-password: RGv80aF9WAJJtExn
db-port: "5432"
db-username: relation_id_4

Read more: Juju | juju run

Congratulations, you now know how to expose operational tasks via actions!

Review the final code

For the full code see: 06_create_actions

For a comparative view of the code before and after this doc see: Comparison

See next: Observe your charm with COS Lite

Contributors: @beliaev-maksim