Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: fix links that pointed to earlier Juju docs #1575

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion docs/explanation/holistic-vs-delta-charms.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ Only some events make sense to handle holistically. For example, `remove` is tri

Similarly, events like `secret-expired` and `secret-rotate` don't make sense to handle holistically, because the charm must do something specific in response to the event. For example, Juju will keep triggering `secret-expired` until the charm creates a new secret revision by calling [`event.secret.set_content()`](ops.Secret.set_content).

This is very closely related to [which events can be `defer`red](https://juju.is/docs/sdk/how-and-when-to-defer-events). A good rule of thumb is this: if an event can be deferred, it may make sense to handle it holistically.
This is very closely related to [which events can be `defer`red](#how-and-when-to-defer-events). A good rule of thumb is this: if an event can be deferred, it may make sense to handle it holistically.

On the other hand, if an event cannot be deferred, the charm cannot handle it holistically. This applies to action "events", `stop`, `remove`, `secret-expired`, `secret-rotate`, and Ops-emitted events such as `collect-status`.
5 changes: 2 additions & 3 deletions docs/explanation/how-and-when-to-defer-events.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
(how-and-when-to-defer-events)=
# How, and when, to defer events

Deferring an event is a common pattern, and when used appropriately is a convenient tool for charmers. However, there are limitations to `defer()` - in particular, that the charm has no way to specify when the handler will be re-run, and that event ordering and context move away from the expected pattern. Our advice is that `defer()` is a good solution for some problems, but is best avoided for others.
Expand All @@ -9,7 +8,7 @@ If the charm encounters a temporary failure (such as working with a container or

Note that it’s important to consider that when the deferred handler is run again, the Juju context may not be exactly the same as it was when the event was first emitted, so the charm code needs to be aware of this.

If the temporary failure is because the workload is busy, and the charm is deployed to a Kubernetes sidecar controller, you might be able to avoid the defer using a [Pebble custom notice](https://juju.is/docs/sdk/interact-with-pebble#heading--use-custom-notices-from-the-workload-container). For example, if the code can’t continue because the workload is currently restarting, if you can have a post-completion hook for the restart that executes `pebble notify`, then you can ensure that the charm is ‘woken up’ at the right time to handle the work.
If the temporary failure is because the workload is busy, and the charm is deployed to a Kubernetes sidecar controller, you might be able to avoid the defer using a [Pebble custom notice](#use-custom-notices-from-the-workload-container). For example, if the code can’t continue because the workload is currently restarting, if you can have a post-completion hook for the restart that executes `pebble notify`, then you can ensure that the charm is ‘woken up’ at the right time to handle the work.

In the future, we hope to see a Juju ‘request re-emit event’ feature that will let the charm tell Juju when it expects the problem to be resolved.

Expand All @@ -33,7 +32,7 @@ In some situations, the charm is waiting for a system to be ready, but it’s no

Deferring the work here is ok, but it’s important to consider the delay between deferring the event and its eventual re-emitting - it’s not safe to assume that this will be a small period of time, unless you know that another event can be expected.

For a Kubernetes charm, If the charm is waiting on the workload and it’s possible to have the workload execute a command when it’s ready, then using a [Pebble custom notice](https://juju.is/docs/sdk/interact-with-pebble#heading--use-custom-notices-from-the-workload-container) is much better than deferring. This then becomes another example of “waiting for a collection of events”, described above.
For a Kubernetes charm, If the charm is waiting on the workload and it’s possible to have the workload execute a command when it’s ready, then using a [Pebble custom notice](#use-custom-notices-from-the-workload-container) is much better than deferring. This then becomes another example of “waiting for a collection of events”, described above.
dwilding marked this conversation as resolved.
Show resolved Hide resolved

## Not possible: actions, shutting down, framework generated events, secrets

Expand Down
2 changes: 1 addition & 1 deletion docs/howto/manage-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ In the `src/charm.py` file, in the `__init__` function of your charm, set up an
self.framework.observe(self.on.cache_storage_attached, self._update_configuration)
```

> See more: [](ops.StorageAttachedEvent), [Juju SDK | Holistic vs delta charms](https://juju.is/docs/sdk/holistic-vs-delta-charms)
> See more: [](ops.StorageAttachedEvent), [](#holistic-vs-delta-charms)

Storage volumes will be automatically mounted into the charm container at either the path specified in the `location` field in the metadata, or the default location `/var/lib/juju/storage/<storage-name>`. However, your charm code should not hard-code the location, and should instead use the `.location` property of the storage object.

Expand Down
2 changes: 1 addition & 1 deletion docs/howto/manage-the-workload-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _on_start(self, event: ops.StartEvent):

## Test the feature

> See first: [Get started with charm testing](https://juju.is/docs/sdk/get-started-with-charm-testing)
> See first: {ref}`get-started-with-charm-testing`

You'll want to add unit and integration tests.

Expand Down
3 changes: 2 additions & 1 deletion docs/howto/run-workloads-with-a-charm-kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ See the [layer specification](https://canonical-pebble.readthedocs-hosted.com/en

#### Add a configuration layer

To add a configuration layer, call [`Container.add_layer`](ops.Container.add_layer) with a label for the layer, and the layer's contents as a YAML string, Python dict, or [`pebble.Layer`](#ops.pebble.Layer) object.
To add a configuration layer, call [`Container.add_layer`](ops.Container.add_layer) with a label for the layer, and the layer's contents as a YAML string, Python dict, or [`pebble.Layer`](ops.pebble.Layer) object.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is out of scope of this PR, but it's the only instance that needs adjusting for consistency, so I'm including it here.


You can see an example of `add_layer` under the ["Replan" heading](#replan). The `combine=True` argument tells Pebble to combine the named layer into an existing layer of that name (or add a layer if none by that name exists). Using `combine=True` is common when dynamically adding layers.

Expand Down Expand Up @@ -823,6 +823,7 @@ Traceback (most recent call last):
ops.pebble.ExecError: non-zero exit code 143 executing ['sleep', '10']
```

(use-custom-notices-from-the-workload-container)=
## Use custom notices from the workload container

### Record a notice
Expand Down
3 changes: 1 addition & 2 deletions docs/howto/turn-a-hooks-based-charm-into-an-ops-charm.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ It is in our interest to move the handler logic for each `/hooks/<hook_name>` to
- We can avoid code duplication by accessing shared data via the CharmBase interface provided through `self`.
- The code is all in one place, easier to maintain.
- We automatically have one Python object we can test, instead of going back and forth between Bash scripts and Python wrappers.
- We can use [the awesome testing Harness](https://juju.is/docs/sdk/testing).
Copy link
Contributor Author

@dwilding dwilding Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning for this change: The preceding bullet already mentions testing. And Harness is not considered so awesome any more 💔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to remove, but you could swap out Scenario for Harness (except you'd need to say "unit testing framework" or some other generic name) and it would still be true.


So let's do that.

Expand Down Expand Up @@ -206,7 +205,7 @@ That allows us to fetch the Relation wherever we need it and access its contents
)
```

Note how `relation.data` provides an interface to the relation databag (more on that [here](https://juju.is/docs/sdk/relations#heading--relation-data)) and we need to select which part of that bag to access by passing an `ops.model.Unit` instance.
Note how `relation.data` provides an interface to the relation databag (see [](ops.Relation.data)) and we need to select which part of that bag to access by passing an `ops.model.Unit` instance.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was going to a Juju reference page previously rather than to the ops API reference. I can't figure out where that is now: @tmihoc did it get removed?

If it doesn't exist any more, I think maybe linking to the section in the relation how-to might be better here?


#### Logging

Expand Down
2 changes: 1 addition & 1 deletion docs/howto/write-unit-tests-for-a-charm.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Notably, specifying relations in `charmcraft.yaml` does not automatically make t
harness. If you have e.g. code that accesses relation data, you must manually add those relations
(including peer relations) for the harness to provide access to that relation data to your charm.

In some cases it may be useful to start the test harness and fire the same hooks that Juju would fire on deployment. This can be achieved using the `begin_with_initial_hooks()` method , to be used in place of the `begin()` method. This method will trigger the events: `install -> relation-created -> config-changed -> start -> relation-joined` depending on whether any relations have been created prior calling `begin_with_initial_hooks()`. An example of this is shown in the [testing relations](https://juju.is/docs/sdk/relations) section.
In some cases it may be useful to start the test harness and fire the same hooks that Juju would fire on deployment. This can be achieved using the `begin_with_initial_hooks()` method , to be used in place of the `begin()` method. This method will trigger the events: `install -> relation-created -> config-changed -> start -> relation-joined` depending on whether any relations have been created prior calling `begin_with_initial_hooks()`. An example of this is shown in [](ops.testing.Harness).

Using the `harness` variable, we can simulate various events in the charm’s lifecycle:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,7 @@ The diagram below illustrates the workflow for the case where the database integ

Now that the charm is getting more complex, there are many more cases where the unit status needs to be set. It's often convenient to do this in a more declarative fashion, which is where the collect-status event can be used.

<!-- TODO: this page doesn't belong in the Juju docs, it should be moved over to ops and this can be a local reference. -->
> Read more: [Events > Collect App Status and Collect Unit Status](https://juju.is/docs/sdk/events-collect-app-status-and-collect-unit-status)
> Read more: [](ops.CollectStatusEvent)

In your charm's `__init__` add a new observer:

Expand Down