How to manage Pebble custom notices¶
Record a notice¶
To record a custom notice, use the pebble notify CLI command. For example, the workload might have a script to back up the database and then record a notice:
pg_dump mydb >/tmp/mydb.sql
/charm/bin/pebble notify canonical.com/postgresql/backup-done path=/tmp/mydb.sql
The first argument to pebble notify is the key, which must be in the format <domain>/<path>. The caller can optionally provide map data arguments in <name>=<value> format; this example shows a single data argument named path.
The pebble notify command has an optional --repeat-after flag, which tells Pebble to only allow the notice to repeat after the specified duration (the default is to repeat for every occurrence). If the caller says --repeat-after=1h, Pebble will prevent the notice with the same type and key from repeating within an hour – useful to avoid the charm waking up too often when a notice occurs frequently.
See more: Pebble |
pebble notify
Respond to a notice¶
To have the charm respond to a notice, observe the pebble_custom_notice event and switch on the notice’s key:
class PostgresCharm(ops.CharmBase):
def __init__(self, framework: ops.Framework):
super().__init__(framework)
# Note that "db" is the workload container's name
framework.observe(
self.on['db'].pebble_custom_notice, self._on_pebble_custom_notice
)
def _on_pebble_custom_notice(
self, event: ops.PebbleCustomNoticeEvent
) -> None:
if event.notice.key == 'canonical.com/postgresql/backup-done':
path = event.notice.last_data['path']
logger.info('Backup finished, copying %s to the cloud', path)
f = event.workload.pull(path, encoding=None)
s3_bucket.upload_fileobj(f, 'db-backup.sql')
elif event.notice.key == 'canonical.com/postgresql/other-thing':
logger.info('Handling other thing')
All notice events have a notice property with the details of the notice recorded. That is used in the example above to switch on the notice key and look at its last_data (to determine the backup’s path).
Fetch notices¶
A charm can also query for notices using the following two Container methods:
get_notice, which gets a single notice by unique ID (the value ofnotice.id).get_notices, which returns all notices by default, and allows filtering notices by specific attributes such askey.
Write unit tests¶
To test charms that use Pebble Notices, use the pebble_custom_notice method to simulate recording a notice with the given details. For example, to simulate the “backup-done” notice handled above, as well as two other notices in the queue, the
charm tests could do the following:
from ops import testing
@patch('charm.s3_bucket.upload_fileobj')
def test_backup_done(upload_fileobj):
# Arrange:
ctx = testing.Context(PostgresCharm)
notice = testing.Notice(
'canonical.com/postgresql/backup-done',
last_data={'path': '/tmp/mydb.sql'},
)
container = testing.Container(
'db',
can_connect=True,
notices=[
testing.Notice(key='example.com/a', occurrences=10),
testing.Notice(key='example.com/b'),
notice,
],
)
root = container.get_filesystem()
(root / 'tmp').mkdir()
(root / 'tmp' / 'mydb.sql').write_text('BACKUP')
state_in = testing.State(containers={container})
# Act:
state_out = ctx.run(
ctx.on.pebble_custom_notice(container, notice), state_in
)
# Assert:
upload_fileobj.assert_called_once()
upload_f, upload_key = upload_fileobj.call_args.args
self.assertEqual(upload_f.read(), b'BACKUP')
self.assertEqual(upload_key, 'db-backup.sql')