--- tocdepth: 3 --- (hook)= # Hook In Juju, a **hook** is a notification from the controller agent through the unit agent to the charm that the internal representation of Juju has changed in a way that requires a reaction from the charm so that the unit's state and the controller's state can be reconciled. For a charm written with {ref}`Ops `, Juju hooks are translated into Ops events = 'events', specifically, into classes that inherit from [`HookEvent`](https://ops.readthedocs.io/en/latest/index.html#ops.HookEvent). ## List of hooks > [Source](https://github.com/juju/juju/blob/main/internal/charm/hooks/hooks.go) (hook-action-action)= ### `-action` #### What triggers it? A charm user invoking the action name from the Juju CLI (`juju run foo`, `juju run foo`). #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? All the units that the charm user has run the action on. (hook-collect-metrics)= ### `collect-metrics` (deprecated) Exposes metrics for external monitoring. (hook-config-changed)= #### `config-changed` #### What triggers it? The `config-changed` hook always runs once immediately after the `install` hook, and likewise after the `upgrade-charm` hook. It also runs whenever the service configuration changes, and when recovering from transient unit agent errors. The `config-changed` event is emitted in response to various events throughout a charm’s lifecycle: - In response to a configuration change using the GUI or CLI. - On networking changes (if the machine reboots and comes up with a different IP). - Some time between the `install` event and the `start` event in the {ref}`startup phase of a charm's lifecycle `.
(The `config_changed` event will **ALWAYS** happen at least once, when the initial configuration is accessed from the charm.) Callbacks associated with this event should ensure the current charm configuration is properly reflected in the underlying application configuration. Invocations of associated callbacks should be idempotent and should not make changes to the environment, or restart services, unless there is a material change to the charm's configuration, such as a change in the port exposed by the charm, addition or removal of a relation which may require a database migration or a "scale out" event for high availability, or similar. Callbacks must not assume that the underlying applications or services have been started. There are many situations in which `config-changed` can occur. In many of them, the event being fired does not mean that the config has in fact changed, though it may be useful to execute logic that checks and writes workload configuration. For example, since `config-changed` is guaranteed to fire once during the startup sequence, some time after `install` is emitted, charm authors might omit a call to write out initial workload configuration during the `install` hook, relying on that configuration to be written out in their `config-changed` handler instead. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Create unit | `juju deploy foo`
`juju add-unit foo` | `install -> config-changed -> start` | | Configure a unit | `juju config foo bar=baz` | `config-changed` | #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-container-pebble-change-updated)= ### `-pebble-change-updated` #### What triggers it? #### Which hooks can be guaranteed to have fired before it, if any? #### Which environment variables is it executed with? #### Who gets it? (hook-container-pebble-check-failed)= ### `-pebble-check-failed` > Kubernetes sidecar charms only. > > Added in Juju 3.6. #### What triggers it? A Pebble check passing the failure threshold. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? (hook-container-pebble-check-recovered)= ### `-pebble-check-recovered` > Kubernetes sidecar charms only. > > Added in Juju 3.6. #### What triggers it? A Pebble check passing after previously reaching the failure threshold. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-container-pebble-custom-notice)= ### `-pebble-custom-notice` > Kubernetes sidecar charms only #### What triggers it? A Pebble notice of type "custom" occurring. #### Which hooks can be guaranteed to have fired before it, if any? #### Which environment variables is it executed with? #### Who gets it? (hook-container-pebble-ready)= ### `-pebble-ready` > Kubernetes sidecar charms only. #### What triggers it? The requested container being ready. The `pebble-ready` event doesn't guarantee the workload container is *still* up. For example, if you manually `kubectl patch` during (for example) `install`, then you may receive this event after the old workload is down but before the new one is up. For this reason it's essential, even in `pebble-ready` event handlers, to catch [`ConnectionError`](https://ops.readthedocs.io/en/latest/pebble.html#ops.pebble.ConnectionError) when using Pebble to make container changes. There is a [`Container.can_connect`()](https://ops.readthedocs.io/en/latest/#ops.Container.can_connect) method, but note that this is a point-in-time check, so just because `can_connect()` returns `True` doesn’t mean it will still return `True` moments later. So, **code defensively** to avoid race conditions. Moreover, as pod churn can occur at any moment, `pebble-ready` events can be received throughout any phase of [a charm's lifecycle](https://juju.is/docs/sdk/a-charms-life). Each container could churn multiple times, and they all can do so independently from one another. In short, the charm should make no assumptions at all about the moment in time at which it may or may not receive `pebble-ready` events, or how often that can occur. The fact that the charm receives a `pebble-ready` event indicates that the container has just become ready (for the first time, or again, after pod churn), therefore you typically will need to **reconfigure your workload from scratch** every single time. This feature of `pebble-ready` events make them especially suitable for a [holistic handling pattern](https://discourse.charmhub.io/t/deltas-vs-holistic-charming/11095). #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-endpoint-relation-broken)= ### `-relation-broken` #### What triggers it? - A non-peer relation being removed; - a unit participating in a non-peer relation being removed, even if the relation is otherwise still alive (through other units); or - an application involved in a non-peer relation being removed. This hook is fired only once per unit per relation and is the exact inverse of `relation-created`. `relation-created` indicates that relation data can be accessed; `relation-broken` indicates that relation data can no longer be read-written. The hook indicates that the relation under consideration is no longer valid, and that the charm’s software must be configured as though the relation had never existed. It will only be called after every hook bound to `-relation-departed` has been run. If a hook bound to this event is being executed, it is guaranteed that no remote units are currently known locally. > It is important to note that **the `-broken` hook might run even if no other units have ever joined the relation**. This is not a bug: even if no remote units have ever joined, the fact of the unit’s participation can be detected in other hooks via the `relation-ids` tool, and the `-broken` hook needs to execute to allow the charm to clean up any optimistically-generated configuration. > Also, it’s important to internalise the fact that there may be multiple relations in play with the same name, and that they’re independent: one `-broken` hook does not mean that *every* such relation is broken. > For a peer relation, `-relation-broken` will never fire, not even during the teardown phase. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-endpoint-relation-changed)= ### `-relation-changed` #### What triggers it? The `relation-changed` hook for a given unit always runs once immediately following the `relation-joined` hook for that unit, and subsequently whenever the related unit changes its settings (by calling `relation-set` and exiting without error). Note that immediately only applies within the context of this particular runtime relation -- that is, when `foo-relation-joined` is run for unit `bar/99` in relation id `foo:123`, the only guarantee is that/ the next hook to be run *in relation id `foo:123`* will be `foo-relation-changed` for `bar/99`. Unit hooks may intervene, as may hooks for other relations, and even for other foo relations. `relation-changed` is emitted when another unit involved in the relation (from either side) touches the relation data. Relation data is *the* way for charms to share non-sensitive information (for sensitive information, `juju secrets` are on their way in juju 3). > For centralized data -- for example, a single password or api token that one application generates to share with another application, we suggest that charm authors use the application data, rather than individual unit data. This data can only be written to by the application leader, and each remote unit related to that application will receive a single `relation-changed` event when it changes. Hooks bound to this event should be the only ones that rely on remote relation settings. They should not error if the settings are incomplete, since it can be guaranteed that when the remote unit or application changes its settings again, this event will fire once more. Charm authors should expect this event to fire many times during an application's life cycle. Units in an application are able to update relation data as needed, and a `relation-changed` event will fire every time the data in a relation changes. Since relation data can be updated on a per unit bases, a unit may receive multiple `relation-changed` events if it is related to multiple units in an application and all those units update their relation data. This event is guaranteed to follow immediately after each [`relation-joined`](https://discourse.charmhub.io/t/relation-name-relation-joined-event/6478). So all `juju` commands that trigger `relation-joined` will also cause `relation-changed` to be fired. So typical scenarios include: | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Add an integration | `juju integrate foo:a bar:b` | (all `foo` and `bar` units)
`*-relation-created -> *-relation-joined -> *-relation-changed`| Additionally, a unit will receive a `relation-changed` event every time another unit involved in the relation changes the relation data. Suppose `foo` receives an event, and while handling it the following block executes: ```python # in charm `foo` relation.data[self.unit]['foo'] = 'bar' # set unit databag if self.unit.is_leader(): relation.data[self.app]['foo'] = 'baz' # set app databag ``` When the hook returns, `bar` will receive a `relation-changed` event. ```{note} Note that units only receive `relation-changed` events for **other** units' changes. This can matter in a peer relation, where the application leader will not receive a `relation-changed` event for the changes that it writes to the peer relation's application data bag. If all units, including the leader, need to react to a change in that application data, charm authors may include an inline `.emit()` for the `_relation_changed` event on the leader. ``` > **When is data synchronized?**
Relation data is sent to the controller at the end of the hook's execution. If a charm author writes to local relation data multiple times during the a single hook run, the net change will be sent to the controller after the local code has finished executing. The controller inspects the data and determines whether the relation data has been changed. Related units then get the `relation-changed` event the next time they check in with the controller. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-endpoint-relation-created)= ### `-relation-created` #### What triggers it? `relation-created` is a "setup" event and, emitted when an application is related to another. Its purpose is to inform the newly related charms that they are entering the relation. If Juju is aware of the existence of the relation "early enough", before the application has started (i.e. *before* the application has started, i.e., before the {ref}`start ` has run), this event will be fired as part of the setup phase. An important consequence of this fact is, that for all peer-type relations, since juju is aware of their existence from the start, those `relation-created` events will always fire before `start`. Similarly, if an application is being scaled up, the new unit will see `relation-created` events for all relations the application already has during the Setup phase. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Integrate | `juju integrate foo bar` | (all foo & bar units): `*-relation-created --> *-relation-joined -> *-relation-changed` | | Scale up an integrated app | `juju add-unit -n1 foo` | (new foo unit): `install -> *-relation-created -> leader-settings-changed -> config-changed -> start` | In the following scenario, one deploys two applications and relates them "very early on". For example, in a single command. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Deploy and quickly integrate | `juju deploy foo; juju deploy bar; juju integrate foo bar` | (all units): same as previous case. | Starting from when `*-relation-created` is received, relation data can be read-written by units, up until when the corresponding `*-relation-broken` is received. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-endpoint-relation-departed)= ### `-relation-departed` #### What triggers it? -relation-departed; emitted when a unit departs from an existing relation. The "relation-departed" hook for a given unit always runs once when a related unit is no longer related. After the "relation-departed" hook has run, no further notifications will be received from that unit; however, its settings will remain accessible via relation-get for the complete lifetime of the relation. `relation-departed` is a "teardown" event, emitted when a remote unit departs a relation. This event is the exact inverse of `relation-joined`. `*-relation-broken` events are emitted on a unit when a related application is scaled down. Suppose you have two related applications, `foo` and `bar`. If you scale down `bar`, all `foo` units will receive a `*-relation-departed` event. The departing unit will receive a `*-relation-broken` event as part of its {ref}`teardown sequence `. Also removing a relation altogether will trigger `*-relation-departed` events (followed by `*-relation-broken`) on all involved units. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Unit removal | `juju remove-unit --num-units 1 bar` | (foo/0): `*-relation-departed`
(bar/0): `*-relation-broken -> stop -> ...` | | Integration removal | `juju remove-relation foo bar` | (all units): `*-relation-departed -> *-relation-broken` | Of course, removing the application altogether, instead of a single unit, will have a similar effect and also trigger these events. Both relation-departed and relation-broken will always fire, regardless of how the relation is terminated. ```{note} For a peer relation, the relation itself will never be destroyed until the application is removed and no units remain, at which point there won't be anything to call the relation-broken hook on anyway. ``` Note that on relation removal (`juju remove-relation`); only when all `-departed` hooks for such a relation and all callback methods bound to this event have been run for such a relation, the unit agent will fire `relation-broken`. The `relation-departed` event is seen both by the leaving unit(s) and the remaining unit(s): - For remaining units (those which have joined and not yet departed), this event is emitted once for each departing unit and in no particular order. At the point when a remaining unit receives a `relation-departed`, it's perfectly probable (although not guaranteed) that the system running that unit has already shut down. - For departing units, this event is emitted once for each remaining unit. A unit's relation settings persist beyond its own departure from the relation; the final unit to depart a relation marked for termination is responsible for destroying the relation and all associated data. `relation-changed` ISN'T fired for removed relations. If you want to know when to remove a unit from your data, that would be relation-departed. > During a `relation-departed` hook, relation settings can still be read (with relation-get) and a relation can even still be set (with relation-set), by explicitly providing the relation ID. All units will still be able to see all other units, and any unit can call relation-set to update their own published set of data on the relation. However, data updated by the departing unit will not be published to the remaining units. This is true even if there are no units on the other side of the relation to be notified of the change. If any affected unit publishes new data on the relation during the relation-departed hooks, the new data will NOT be see by the departing unit (it will NOT receive a relation-changed; only the remaining units will). ```{note} (juju internals) When a unit's own participation in a relation is known to be ending, the unit agent continues to uphold the guaranteed event ordering, but within those constraints, it will run the fewest possible hooks to notify the charm of the departure of each individual remote unit. ``` #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-relation-joined)= ### `-relation-joined` #### What triggers it? -relation-joined; emitted when a new unit joins in an existing relation. The "relation-joined" hook always runs once when a related unit is first seen. `relation-joined` is emitted when a unit joins in an existing relation. The unit will be a local one in the case of peer relations, a remote one otherwise. By the time this event is emitted, the only available data concerning the relation is - the name of the joining unit. - the `private-address` of the joining unit. In other words, when this event is emitted the remote unit has not yet had an opportunity to write any data to the relation databag. For that, you're going to have to wait for the first {ref}``relation-changed` ` event. From the perspective of an application called `foo`, which can relate to an application called `bar`: | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Create unit | `juju integrate foo bar` | `*-relation-created -> *-relation-joined -> *-relation-changed` | | Create unit | `juju add-unit bar -n 1` | `*-relation-joined -> *-relation-changed`| ```{note} For a peer relation, `-relation-joined` will be received by peers some time after a new peer unit appears. (And during its setup, that new unit will receive a `-relation-created`). ``` ```{note} For a peer relation, `-relation-joined` will only be emitted if the scale is larger than 1. In other words, applications with a scale of 1 do not see peer relation joined/departed events. **If you are using peer data as a means for persistent storage, then use peer `relation-created` instead**. ``` `relation-joined` can fire multiple times per relation, as multiple units can join, and is the exact inverse of `relation-departed`. That means that if you consider the full lifecycle of an application, a unit, or a model, the net difference of the number of `*-relation-joined` events and the number of `*-relation-departed` events will be zero. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-install)= ### `install` #### What triggers it? The `install` hook always runs once, and only once, before any other hook. fired when juju is done provisioning the unit. The `install` event is emitted once per unit at the beginning of a charm's lifecycle. Associated callbacks should be used to perform one-time initial setup operations and prepare the unit to execute the application. Depending on the charm, this may include installing packages, configuring the underlying machine or provisioning cloud-specific resources. Therefore, ways to cause `install` to occur are: | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Create unit | `juju deploy foo`
`juju add-unit foo` | `install -> config-changed`| > Note: > - Typically, operations performed on `install` should also be considered for [`upgrade-charm`](https://discourse.charmhub.io/t/upgrade-charm-event/6485). > - In some cases, [`config-changed`](https://discourse.charmhub.io/t/config-changed-event/6465) can be used instead of `install` and `upgrade-charm` because it is guaranteed to fire after both. The `install` event is emitted once per unit at the beginning of a charm's lifecycle. Associated callbacks should be used to perform one-time initial setup operations and prepare the unit to execute the application. Depending on the charm, this may include installing packages, configuring the underlying machine or provisioning cloud-specific resources. Therefore, ways to cause `install` to occur are: | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Create unit | `juju deploy foo`
`juju add-unit foo` | `install -> config-changed`| > Note: > - Typically, operations performed on `install` should also be considered for [`upgrade-charm`](https://discourse.charmhub.io/t/upgrade-charm-event/6485). > - In some cases, [`config-changed`](https://discourse.charmhub.io/t/config-changed-event/6465) can be used instead of `install` and `upgrade-charm` because it is guaranteed to fire after both. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-leader-deposed)= ### `leader-deposed` #### What triggers it? TBA #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-leader-elected)= ### `leader-elected` #### What triggers it? The `leader-elected` event is emitted for a unit that is elected as leader. Together with `leader-settings-changed`, it is one of two "leadership events". A unit receiving this event can be guaranteed that it will have leadership for approximately 30 seconds (from the moment the event is received). After that time, juju *might* have elected a different leader. The same holds if the unit checks leadership by `Unit.is_leader()`: if the result is `True`, then the unit can be ensured that it has leadership for the next 30s. > Leadership can change while a hook is running. (You could start a hook on unit/0 who is the leader, and while that hook is processing, you lose network connectivity for a long time [more than 30s], and then by the time the hook notices, juju has already moved on to another leader.) > Juju doesn't guarantee that a leader will see every event: if the leader unit is overloaded long enough for the lease to expire (>30s), then juju will elect a different leader. Events that fired in between would be received units that are not leader yet or not leader anymore. - `leader-elected` is always emitted **after** peer-`relation-created` during the Startup phase. However, by the time `relation-created` runs, juju may already have a leader. This means that, in peer-relation-created handlers, it might already be the case that `self.unit.is_leader()` returns `True` even though the unit did not receive a leadership event yet. If the starting unit is *not* leader, it will receive a {ref}``leader-settings-changed` ` instead. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Start new unit | `juju deploy foo`
`juju add-unit foo` | (new leader) `install -> (*peer)-relation-created -> leader-elected`| - During the Operation phase, leadership changes can in principle occur at any time, for example if the leader unit is unresponsive for some time. When a leader loses leadership it will only receive a `leader-settings-changed` event, just like all the other non-leader units. The new leader will receive `leader-elected`. > It is not possible to select a specific unit and 'promote' that unit to leader, or 'demote' an existing leader unit. Juju has control over which unit will become leader after the current leader is gone. ```{note} However, you can cause leadership change by destroying the leader unit or killing the jujud-machine service in operator charms. - non-k8s models: `juju remove-unit ` - operator charms: `juju ssh -m -- systemctl stop jujud-machine-` - sidecar charms: ssh into the charm container, source the `/etc/profile.d/juju-introspection.sh` script, and then get access to a few cli tools, including `juju_stop_unit`. That will cause the lease to expire within 60s, and another unit of the same application will be elected leader and receive `leader-elected`. ``` - If the leader unit is removed, then one of the remaining units will be elected as leader and see the `leader-elected` event; all the other remaining units will see `leader-settings-changed`. If the leader unit was not removed, no leadership events will be fired on any units. > Note that unless there's only one unit left, it is impossible to predict or control which one of the remaining units will be elected as the new leader. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Current leader loses leadership | `juju remove-unit foo` | (new leader): `leader-elected`
(all other foo units): `leader-settings-changed`| #### Which hooks can be guaranteed to have fired before it, if any? #### Which environment variables is it executed with? #### Who gets it? The leader unit, once Juju elects one. (hook-leader-settings-changed)= ### `leader-settings-changed` #### What triggers it? The `leader-settings-changed` event is emitted when a leadership change occurs, all units that are not the new leader will receive the event. Also, this event is emitted if changes have been made to leader settings. During startup sequence, for all non-leader units: | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Create unit | `juju deploy foo -n 2` | `install -> leader-settings-changed -> config-changed -> start` (non-leader)| If the leader unit is rescheduled, or removed entirely. When the new leader is elected: | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Removal of leader | `juju remove-unit foo/0` (foo/0 being leader) | `leader-settings-changed` (for all non leaders) | > Since this event needs leadership changes to trigger, check out {ref}`triggers for `leader-elected` <6471md>` as the same situations apply for `leader-settings-changed`. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? All follower units, when a new leader is chosen. (hook-post-series-upgrade)= ### `post-series-upgrade` > Removed in Juju 4. #### What triggers it? Fired after the series upgrade has taken place. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-pre-series-upgrade)= ### `pre-series-upgrade` > Removed in Juju 4. #### What triggers it? This event is triggered when an operator runs `juju upgrade-series prepare ...` from the command line. Read [here](https://juju.is/docs/olm/upgrade-a-machines-series#heading--upgrading-a-machines-series) to learn more about the series upgrade process. This event hook allows charm units on the machine being upgraded to do any necessary tasks prior to the upgrade process beginning (which may involve e.g. being rebooted, etc.). | Scenario | Example command | Resulting events | |:-:|-|-| |[Upgrade series](https://juju.is/docs/olm/upgrade-a-machines-series#heading--upgrading-a-machines-series) | `juju upgrade-series prepare`| `pre-series-upgrade` -> (events on pause until upgrade completes) | Notably, after this event fires and before the [post-series-upgrade event](https://discourse.charmhub.io/t/post-series-upgrade-event/6472) fires, juju will pause events and changes for all units on the machine being upgraded. There will be no config-changed, update-status, etc. events to interrupt the upgrade process until after the upgrade process is completed via `juju upgrade-series complete`. ```{caution} Leadership is pinned during the series upgrade process. Even if the current leader dies or is removed, re-election will not occur for applications on the upgrading machine until the series upgrade operation completes. ``` #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-remove)= ### `remove` #### What triggers it? The `remove` event is emitted only once per unit: when the Juju controller is ready to remove the unit completely. The `stop` event is emitted prior to this, and all necessary steps for handling removal should be handled there. On Kubernetes charms, the `remove` event will occur on pod churn, when the unit dies. On machine charms, the stop event will be fired when a unit is put down. | Scenario | Example Command | Resulting Events | | :-------: | -------------------------- | ------------------------------------ | | Remove unit | `juju remove-unit foo/0` (on machine) or
`juju remove-unit --num-units 1 foo` (on k8s) | `stop -> [relation/storage teardown] -> remove` | Of course, removing an application altogether will result in these events firing on all units. If the unit has any relations active or any storage attached at the time the removal occurs, these will be cleaned up (in no specific order) between `stop` and `remove`. This means the unit will receive `stop -> (*-relation-broken | *-storage-detaching) -> remove`. The `remove` event is the last event a unit will ever see before going down, right after [`stop`](https://discourse.charmhub.io/t/6483). It is exclusively fired when the unit is in the [Teardown phase](https://discourse.charmhub.io/t/5938). #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-secret-changed)= ### `secret-changed` #### What triggers it? A secret owner publishing a new secret revision. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? All units observing a secret. ```{note} Upon receiving that event (or at any time after that) an observer can choose to: - Start tracking the latest revision ("refresh") - Inspect the latest revision values, without tracking it just yet ("peek") Once all observers have stopped tracking a specific outdated revision, the owner will receive a `secret-remove` hook to be notified of that fact, and can then remove that revision. ``` (hook-secret-expired)= ### `secret-expired` > Currently supported only for charm secrets. > > Added in Juju 3.0.2 #### What triggers it? For a secret set up with an expiry date, the fact of the secret’s expiration time elapsing. #### Which hooks can be guaranteed to have fired before it, if any? #### Which environment variables is it executed with? #### Who gets it? The secret owner. (hook-secret-remove)= ### `secret-remove` > Currently supported only for charm secrets. #### Who gets it? The secret owner. #### What triggers it? A secret revision no longer having any observers and thus being safe to remove. This situation can occur when: - All observers tracking a now-outdated revision have updated to tracking a newer one, so the old revision can be removed. - No observer is tracking an intermediate revision, and a newer one has already been created. So there is a orphaned revision which no observer will ever be able to peek or update to, because there is already a newer one the observer would get instead. In short, the purpose of this event is to notify the owner of a secret that a specific revision of it is safe to remove: no charm is presently observing it or ever will be able to in the future. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA (hook-secret-rotate)= ### `secret-rotate` > Currently supported only for charm secrets. > > Added in Juju 3.0.2 #### What triggers it? For a secret with a rotation policy, the secret's rotation policy elapsing -- the hook keeps firing until the secret has been rotated. #### Which hooks can be guaranteed to have fired before it, if any? #### Which environment variables is it executed with? #### Who gets it? The secret owner. (hook-start)= ### `start` #### What triggers it? A unit's initialization being complete. This can occur: - when the unit is being created - (Kubernetes:) on pod churn - (Kubernetes:) on cluster reboot - on charm upgrades For Kubernetes charms, this occurs on pod churn as well. ```{note} Callback methods bound to the event should be used to ensure that the charm’s software is in a running state. Note that the charm’s software should be configured to persist in a started state without further intervention from Juju or an administrator. ``` ```{note} In kubernetes sidecar charms, Juju provides no ordering guarantees regarding `start` and `*-pebble-ready`. ``` #### Which hooks can be guaranteed to have fired before it, if any? The `config-changed` hook. (The `start` hook is fired immediately after.) #### Which environment variables is it executed with? #### Who gets it? Any unit. (hook-stop)= ### `stop` #### What triggers it? The Juju controller being ready to destroy the unit. This can occur: - when the unit is being removed (whether explicitly or through the application as a whole being removed) - (Kubernetes:) on pod churn The `stop` hook is the one-before-last hook the unit will receive before being destroyed (the last one being `remove`). ```{note} When handling the `stop` event, charms should gracefully terminate all services for the supported application and update any relevant cluster/leader information to remove or update any data relating to the current unit. Additionally, the charm should ensure that the software will not automatically start again on reboot. ``` #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? Any unit. (hook-storage-storage-attached)= ### `-storage-attached` #### What triggers it? A storage volume having been attached to the charm's host machine or container and being ready to be interacted with. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-storage-storage-detaching)= #### `-storage-detaching` #### What triggers it? A request to detach storage having been processed. #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? TBA (hook-update-status)= ### `update-status` #### What triggers it? Nothing in particular -- this hooks is fired automatically by Juju at regular intervals (default: 5m; can be changed, e.g., `juju model-config update-status-hook-interval=1m`). ```{note} This event can be used to monitor the health of deployed charms and determine the status of long running tasks (such as package installation), updating the status message reported to Juju accordingly. Historically, this hook was intended to allow authors to run code that gets the “health” of the application. However, health checks can also be specified via {ref}`pebble`. Since the update-status interval is model-wide (not per application) and is set by the user (for example, it can be set to once per hour), charms should not rely on it for critical operations. In integration tests, unless specifically testing the update-status hook, you may want to "disable" it so it doesn't interfere with the test. This can be achieved by setting the interval to e.g. 1h at the beginning of the test. ``` #### Which hooks can be guaranteed to have fired before it, if any? As it is triggered periodically, the `update-status` can happen in-between any other charm events. #### Which environment variables is it executed with? TBA #### Who gets it? TBA By default, the `update-status` event is triggered by the Juju controller at 5-minute intervals. (hook-upgrade-charm)= ### `upgrade-charm` #### What triggers it? The `upgrade-charm` hook always runs once immediately after the charm directory contents have been changed by an unforced charm upgrade operation, and *may* do so after a forced upgrade; but will *not* be run after a forced upgrade from an existing error state. (Consequently, neither will the `config-changed` hook that would ordinarily follow the `upgrade-charm`. The event is emitted after the new charm code has been unpacked - therefore this event is handled by the callback method bound to the event in the new codebase. The associated callback should be used to reconcile the current state written by an older version into whatever form is required by the current charm version. An example of a reconciliation that needs to take place is to migrate an old relation data schema to a new one. ```{important} - Typically, operations performed on `upgrade-charm` should also be considered for [`install`](https://discourse.charmhub.io/t/install-event/6469). - In some cases, [`config-changed`](https://discourse.charmhub.io/t/config-changed-event/6465) can be used instead of `install` and `upgrade-charm` because it is guaranteed to fire after both. - Note that you cannot upgrade a Charmhub charm to the same version. However, upgrading a local charm from path works (and goes through the entire upgrade sequence) even if the charm is exactly the same. ``` | Scenario | Example command | Resulting events | |:-:|-|-| |Upgrade Kubernetes charm | `juju refresh` | `stop` (old charm) -> `upgrade-charm` (new charm) -> `config-changed` -> `leader-settings-changed` (if the unit is not the leader) -> `start` -> `*-pebble-ready`| |Upgrade machine charm | `juju refresh` | `upgrade-charm` -> `config-changed` -> `leader-settings-changed` (if the unit is not the leader) -> `start`| |Attach resource | `juju attach-resource foo bar=baz` | (same as upgrade) | ```{important} An upgrade does NOT trigger any relation hooks (unless relation data is intentionally modified in one of the upgrade sequence hooks). ``` #### Which hooks can be guaranteed to have fired before it, if any? TBA #### Which environment variables is it executed with? TBA #### Who gets it? Any unit.