Actions are methods defined by a charm developer designed to be invoked by an administrator against a deployed charm. They are commonly used to expose maintenance or operations tasks, such as creating a snapshot of a database, adding a user to a system or dumping debug information.
Actions are defined in the actions.yaml
file. As with configuration, each action is defined as a top-level key of a YAML map. The name of the key is the name of the action, and corresponds to a map of fields that define the action.
Action names are validated to ensure they do not collide with Python keywords, and that they are valid identifiers:
- Identifiers contain only alphanumeric characters or underscores
- First character must not be a digit
- Action names may also include hyphens
Each action should define a description, and can optionally define some parameters in a YAML map named params
, which is a JSON Schema transformed into a YAML map. Parameters can contain nested JSON schema.
Some notes on the use of JSON Schema in actions.yaml
:
- The
$schema
and$ref
keys from JSON schema are not currently supported - The
additionalProperties
andrequired
keys from JSON Schema can be used at the top-level of an action (adjacent todescription
andparams
), but also used anywhere within a nested schema
Contents:
Example action definitions
The following illustrate some example action definitions:
Simple action 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
:
pause:
description: Pause the database.
resume:
description: Resume a paused database.
snapshot:
description: Take a snapshot of the database.
params:
outfile:
type: string
description: The filename to write to.
Complex action example
The following example showcases a more complex configuration file that uses a nested schema to define detailed options. It also mandates that the action should not run if extra parameters are provided (additionalProperties: false
) and makes the filename
field mandatory:
pause:
description: Pause the database.
resume:
description: Resume a paused database.
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
An administrator would invoke this action like so:
$ juju run <unit> snapshot filename=out.tar.gz compression.kind=gzip
You can opt-in to a nicer actions user experience at the command line by using a feature flag with Juju. Read more on Discourse. This will become the default in future releases of Juju.
Action handling
Actions are surfaced as events that a charm can observe, similarly to lifecycle events. Action names are parsed by the Charmed Operator Framework and surfaced as events named <action_name>_action
.
If hyphens are used in action names, they are replaced with underscores in the corresponding event names. For example, an action named snapshot-database
would result in an event named snapshot_database_action
being triggered when the action is invoked.
Action handlers are passed an ActionEvent
as their first parameter, and thus have access to the action parameter values through the <event>.params
construct. In addition to the params
dict, the ActionEvent
class provides three convenience methods to the developer:
-
<event>.fail(message="")
: Report to the administrator that the action has failed, optionally providing a message indicating why the failure occured. -
<event>.log(message)
: Log a message for the administrator (available while the action is running). -
<event>.set_results(results)
: Report the result of the action, whereresults
is a dictionary.
Messages are displayed to administrators in real time when they run juju run-action
with the --wait
flag. For more information on working with actions as an administrator see Juju OLM | How to manage actions.
When you run an action via juju run
, you either select a specific unit to run the action, or all units in your Juju application. Inside the unit(s) that will run the action, how the action is run depends on the type of the charm. If your charm is a machine charm, actions are executed on the same machine as the application. If your charm is a Kubernetes charm implementing the sidecar pattern, the charm action is run in the charm container.
Action handling example
For the following actions.yaml
:
grant-admin-role:
description: >
Grant the "system_admin" role to a user. The user will need to log out and
log back in to realise their permissions upgrade.
params:
user:
type: string
description: The user to grant "system_admin" role to.
required: [user]
A charm developer could handle the action invocation like so:
class ActionsCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
# ...
self.framework.observe(self.on.grant_admin_role_action, self._on_grant_admin_role_action)
# ...
def _on_grant_admin_role_action(self, event):
"""Handle the grant-admin-role action."""
# Fetch the user parameter from the ActionEvent params dict
user = event.params["user"]
# Do something useful with it
cmd = ["/usr/bin/myapp", "roles", "system_admin", user]
# Set a log message for the action
event.log(f"Running this command: {' '.join(cmd)}")
granted = subprocess.run(cmd, capture_output=True)
if granted.returncode != 0:
# Fail the action if there is an error
event.fail(
f"Failed to run '{' '.join(cmd)}'. Output was:\n{granted.stderr.decode('utf-8')}"
)
else:
# Set the results of the action
msg = f"Ran grant-admin-role for user '{user}'"
event.set_results({"result": msg})