Skip to content

Commit

Permalink
Resolve many warnings (writing output).
Browse files Browse the repository at this point in the history
There are many 'cross-reference target not found' warnings where there is no xref - these are all to content that isn't yet published - see the PR description for details.

There is still a huge number of warnings in the earlier stages - I'll do a separate commit for those.
  • Loading branch information
tonyandrewmeyer committed Dec 3, 2024
1 parent 680613f commit 5ad155d
Show file tree
Hide file tree
Showing 20 changed files with 70 additions and 71 deletions.
8 changes: 4 additions & 4 deletions docs/howto/get-started-with-charm-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Testing charm code is an essential part of charming. Here we will see how to get
**What you'll need:**
- knowledge of testing in general
- knowledge of Juju and charms
- knowledge of the Juju models and events, esp. the data involved in a charm's lifecycle (e.g. see {ref}`Talking to a workload control flow from A to Z <talking-to-a-workload-control-flow-from-a-to-z>`)
- knowledge of the Juju models and events, esp. the data involved in a charm's lifecycle (e.g. see [Talking to a workload control flow from A to Z]())

**What you will learn:**
- What are the starting points for adding tests to a charm?
Expand Down Expand Up @@ -77,7 +77,7 @@ If the charm is a machine charm, workload operation calls can be done directly,

"Juju operations" are the most 'meta' of them all: they do not affect the workload in and of itself, but they share data which is meant to affect the operation of *other* charms that this charm is integrated with.

> See more: {ref}`Talking to a workload: control flow from A to Z <talking-to-a-workload-control-flow-from-a-to-z>`, {ref}`Charm lifecycle <charm-lifecycle>`
> See more: [Talking to a workload: control flow from A to Z](), [Charm lifecycle]()
### What we are testing when we unit-test

Expand Down Expand Up @@ -120,7 +120,7 @@ There are two ways to initialize a harnessed charm:
* When a charm is deployed, it goes through the Setup phase, a fixed sequence of events. `Harness` has a method, `begin_with_initial_hooks()`, that runs this sequence.
* Alternatively, you can initialise the charm by calling `begin()`. This will instantiate the charm without firing any Setup phase event.

> See more: {ref}`A charm's life <charm-lifecycle>`, [`ops.testing.Harness.begin_with_initial_hooks()`](https://ops.readthedocs.io/en/latest/harness.html#ops.testing.Harness.begin_with_initial_hooks), [`ops.testing.Harness.begin()`](https://ops.readthedocs.io/en/latest/harness.html#ops.testing.Harness.begin)
> See more: [A charm's life](), [`ops.testing.Harness.begin_with_initial_hooks()`](https://ops.readthedocs.io/en/latest/harness.html#ops.testing.Harness.begin_with_initial_hooks), [`ops.testing.Harness.begin()`](https://ops.readthedocs.io/en/latest/harness.html#ops.testing.Harness.begin)
After the Setup phase, the charm goes into Operation. To test operation-phase-related events, the harness provides some methods to simulate the most common scenarios. For example:

Expand Down Expand Up @@ -256,7 +256,7 @@ A good integration testing suite will check that the charm continues to operate
Some charms represent their workload by means of an object-oriented wrapper, which mediates between operator code and the implementation of operation logic. In such cases, it can be useful to add a third category of tests, namely functional tests, that black-box test that workload wrapper without worrying about the substrate it runs on (the charm, the cloud, the machine or pod...).
For an example charm adopting this strategy, see [parca-operator](https://github.com/jnsgruk/parca-operator). Nowadays, the preferred tool to do functional testing is Scenario.

> See more: [Scenario](https://github.com/canonical/ops-scenario), {ref}`Write a Scenario test for a charm <how-to-write-scenario-tests-for-a-charm>`
> See more: [Scenario](https://github.com/canonical/ops-scenario), {ref}`Write a Scenario test for a charm <write-scenario-tests-for-a-charm>`
## Continuous integration

Expand Down
6 changes: 3 additions & 3 deletions docs/howto/instrument-your-charm-with-tracing-telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ provides:
limit: 1
```
> See more: {ref}`File `charmcraft.yaml` > `provides` <11012md>`
> See more: [File `charmcraft.yaml` > `provides`]()

7. Instrument your charm code:

Expand All @@ -91,8 +91,8 @@ from charms.tempo_k8s.v2.tracing import TracingEndpointRequirer
class MyCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
# add a provider wrapper for the tracing endpoint
self.tracing = TracingEndpointRequirer(self, protocols={ref}`"otlp_http"])
# Add a provider wrapper for the tracing endpoint.
self.tracing = TracingEndpointRequirer(self, protocols=["otlp_http"])
@property
def tracing_endpoint(self) -> Optional[str]:
Expand Down
8 changes: 5 additions & 3 deletions docs/howto/manage-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,19 @@ More detail below:
#### Use action params
To make use of action parameters, either ones that the user has explicitly passed, or default values, use the `params` attribute of the event object that is passed to the handler. This is a dictionary of parameter name (string) to parameter value. For example:
```python
def _on_snapshot(self, event: ops.ActionEvent):
filename = event.params["filename"]
...
```
> See more: [`ops.ActionEvent.params`](https://ops.readthedocs.io/en/latest/#ops.ActionEvent.params)

#### Report that an action has failed

To report that an action has failed, in the event handler definition, use the fail() method along with a message explaining the failure to be shown to the person running the action. Note that the `fail()` method doesn’t interrupt code execution, so you will usually want to immediately follow the call to `fail()` with a `return`, rather than continue with the event handler. For example:

```python
def _on_snapshot(self, event: ops.ActionEvent):
filename = event.params['filename']
Expand All @@ -102,9 +104,9 @@ def _on_snapshot(self, event: ops.ActionEvent):
event.fail(
f"Failed to run {' '.join(cmd)!r}. Output was:\n{granted.stderr.decode('utf-8')}"
)
...
```
]

> See more: [`ops.ActionEvent.fail`](https://ops.readthedocs.io/en/latest/#ops.ActionEvent.fail)
#### Return the results of an action
Expand Down
8 changes: 4 additions & 4 deletions docs/howto/manage-interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,12 @@ def test_nothing_happens_if_remote_empty():
t = Tester(
State(
leader=True,
relations={ref}`
relations={
Relation(
endpoint="my-fancy-database", # the name doesn't matter
interface="my_fancy_database",
)
],
},
)
)
# WHEN the database charm receives a relation-joined event
Expand Down Expand Up @@ -332,7 +332,7 @@ You should see:
In particular, pay attention to the `provider` field. If it says `<no tests>` then there is something wrong with your setup, and the collector isn't able to find your test or identify it as a valid test.

Similarly, you can add tests for requirer in `./interfaces/my_fancy_database/v0/interface_tests/test_requirer.py`. Don't forget to edit the `interface.yaml` file in the "requirers" section to add the name of the charm and the URL. See the "Edit `interface.yaml`" section in the previous how-to guide {ref}`How to register an interface <12690md>` for more detail on editing `interface.yaml`. [Here](https://github.com/IronCore864/charm-relation-interfaces/tree/my-fancy-database/interfaces/my_fancy_database/v0) is an example of tests for requirers added.
Similarly, you can add tests for requirer in `./interfaces/my_fancy_database/v0/interface_tests/test_requirer.py`. Don't forget to edit the `interface.yaml` file in the "requirers" section to add the name of the charm and the URL. See the "Edit `interface.yaml`" section in the previous how-to guide [How to register an interface]() for more detail on editing `interface.yaml`. [Here](https://github.com/IronCore864/charm-relation-interfaces/tree/my-fancy-database/interfaces/my_fancy_database/v0) is an example of tests for requirers added.

### Merge in charm-relation-interfaces

Expand Down Expand Up @@ -451,7 +451,7 @@ INFO:root:Running tests for role: provider
}
```

For reference, [here](https://github.com/IronCore864/my-fancy-database-operator) is an example of a bare minimum `my-fancy-database-operator` charm to make the test pass. In the charm, application relation data and unit relation data are set according to our definition (see the beginning part of the previous how-to guide {ref}`How to register an interface <how-to-register-an-interface>`).
For reference, [here](https://github.com/IronCore864/my-fancy-database-operator) is an example of a bare minimum `my-fancy-database-operator` charm to make the test pass. In the charm, application relation data and unit relation data are set according to our definition (see the beginning part of the previous how-to guide [How to register an interface]().

### Troubleshooting and debugging the tests

Expand Down
2 changes: 1 addition & 1 deletion docs/howto/manage-leadership-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ To have the leader notify other units about leadership changes, change data in a

> See more: [Peer Relations](https://juju.is/docs/juju/relation#heading--peer)
{ref}`note status="Use the peer relation rather than `leader-setting-changed`"]
[note status="Use the peer relation rather than `leader-setting-changed`"]
In the past, this was done by observing a `leader-setting-changed` event, which is now deprecated.
[/note]

Expand Down
4 changes: 2 additions & 2 deletions docs/howto/manage-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ has attached the new storage.

## Test the feature

> See first: {ref}`get-started-with-charm-testing>`
> See first: {ref}`get-started-with-charm-testing`

You'll want to add three levels of tests:

Expand Down Expand Up @@ -161,7 +161,7 @@ def test_storage_detaching(harness):
# Simulate the harness being detached (.remove_storage() would simulate it being removed
# entirely).
harness.remove_storage(storage_id)
# Asser that it was handled correctly.
# Assert that it was handled correctly.
assert ...
```

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 @@ -107,7 +107,7 @@ def test_workload_version_is_set():
"webserver",
exec_mock={("/bin/server", "--version"): scenario.ExecOutput(stdout="1.2\n")},
)
out = ctx.run('start', scenario.State(containers={ref}`container]))
out = ctx.run('start', scenario.State(containers=[container]))
assert out.workload_version == "1.2"
```

Expand Down
10 changes: 5 additions & 5 deletions docs/howto/run-workloads-with-a-charm-kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The recommended way to create charms for Kubernetes is using the sidecar pattern

Pebble is a lightweight, API-driven process supervisor designed for use with charms. If you specify the `containers` field in a charm's `charmcraft.yaml`, Juju will deploy the charm code in a sidecar container, with Pebble running as the workload container's `ENTRYPOINT`.

When the workload container starts up, Juju fires a [`PebbleReadyEvent`](https://ops.readthedocs.io/en/latest/#ops.PebbleReadyEvent), which can be handled using [`Framework.observe`](https://ops.readthedocs.io/en/latest/#ops.Framework.observe) as shown in {ref}`Framework Constructs under "Containers" <4554md>`. This gives the charm author access to `event.workload`, a [`Container`](https://ops.readthedocs.io/en/latest/#ops.Container) instance.
When the workload container starts up, Juju fires a [`PebbleReadyEvent`](https://ops.readthedocs.io/en/latest/#ops.PebbleReadyEvent), which can be handled using [`Framework.observe`](https://ops.readthedocs.io/en/latest/#ops.Framework.observe) as shown in [Framework Constructs under "Containers"](). This gives the charm author access to `event.workload`, a [`Container`](https://ops.readthedocs.io/en/latest/#ops.Container) instance.

The `Container` class has methods to modify the Pebble configuration "plan", start and stop services, read and write files, and run commands. These methods use the Pebble API, which communicates from the charm container to the workload container using HTTP over a Unix domain socket.

Expand Down Expand Up @@ -212,7 +212,7 @@ See the [layer specification](https://github.com/canonical/pebble#layer-specific

To add a configuration layer, call [`Container.add_layer`](https://ops.readthedocs.io/en/latest/#ops.Container.add_layer) with a label for the layer, and the layer's contents as a YAML string, Python dict, or [`pebble.Layer`](https://ops.readthedocs.io/en/latest/#ops.pebble.Layer) object.

You can see an example of `add_layer` under the ["Replan" heading](#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.
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.

Because `combine=True` combines the layer with an existing layer of the same name, it's normally used with `override: replace` in the YAML service configuration. This means replacing the entire service configuration with the fields in the new layer.

Expand Down Expand Up @@ -247,7 +247,7 @@ In the context of Juju sidecar charms, Pebble is run with the `--hold` argument,

### Replan

After adding a configuration layer to the plan (details below), you need to call `replan` to make any changes to `services` take effect. When you execute replan, Pebble will automatically restart any services that have changed, respecting dependency order. If the services are already running, it will stop them first using the normal [stop sequence](#heading--start-and-stop).
After adding a configuration layer to the plan (details below), you need to call `replan` to make any changes to `services` take effect. When you execute replan, Pebble will automatically restart any services that have changed, respecting dependency order. If the services are already running, it will stop them first using the normal [stop sequence](#start-and-stop).

The reason for replan is so that you as a user have control over when the (potentially high-impact) action of stopping and restarting your services takes place.

Expand Down Expand Up @@ -822,7 +822,7 @@ Caution: it's easy to get threading wrong and cause deadlocks, so it's best to u
To send a signal to the running process, use [`ExecProcess.send_signal`](https://ops.readthedocs.io/en/latest/#ops.pebble.ExecProcess.send_signal) with a signal number or name. For example, the following will terminate the "sleep 10" process after one second:

```python
process = container.exec({ref}`'sleep', '10'])
process = container.exec(['sleep', '10'])
time.sleep(1)
process.send_signal(signal.SIGTERM)
process.wait()
Expand All @@ -832,7 +832,7 @@ Note that because sleep will exit via a signal, `wait()` will raise an `ExecErro

```
Traceback (most recent call last):
..
...
ops.pebble.ExecError: non-zero exit code 143 executing ['sleep', '10']
```

Expand Down
Loading

0 comments on commit 5ad155d

Please sign in to comment.