Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions kepler/charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ containers:
demo-server:
resource: demo-server-image

actions:
snapshot:
description: Take a snapshot of the database.
params:
filename:
type: string
description: The name of the snapshot file.
required:
- filename
additionalProperties: false

resources:
# An OCI image resource for the container listed above.
demo-server-image:
Expand Down
17 changes: 17 additions & 0 deletions kepler/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ def __init__(self, framework: ops.Framework) -> None:
super().__init__(framework)
self.pebble_service_name = "fastapi-service"
framework.observe(self.on["demo-server"].pebble_ready, self._on_demo_server_pebble_ready)
framework.observe(self.on["snapshot"].action, self._on_snapshot_action)

def _on_snapshot_action(self, event: ops.ActionEvent) -> None:
"""Handle the snapshot action, mirroring the ops docs example."""
filename = event.params["filename"]
event.log(f"Generating snapshot into {filename}")
success = self.do_snapshot(filename=filename)
if not success:
event.fail("Failed to generate snapshot.")
return
msg = f"Stored snapshot in {filename}."
event.set_results({"result": msg})

def do_snapshot(self, *, filename: str) -> bool:
"""Pretend to take a snapshot of the database."""
del filename # Unused in this demo stub.
return True

def _on_demo_server_pebble_ready(self, event: ops.PebbleReadyEvent) -> None:
"""Define and start a workload using the Pebble API."""
Expand Down
44 changes: 44 additions & 0 deletions kepler/tests/unit/test_snapshot_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# To learn more about testing, see https://documentation.ubuntu.com/ops/latest/explanation/testing/

"""Tests validating the ops 'manage actions' unit-test example.

The docs (https://canonical.com/juju/docs/ops/latest/howto/manage-actions/#write-unit-tests)
show a snapshot action handler that calls ``event.set_results({'result': msg})``
and then a unit test that asserts ``'snapshot-size' in ctx.action_results``.

The handler only ever sets a ``result`` key, so ``snapshot-size`` cannot be
present in the results. This test asserts that understanding: if it passes, the
documented assertion would fail and the doc example is incorrect.
"""

from ops import testing

from charm import KosmosCharm


def test_snapshot_action_does_not_set_snapshot_size():
"""The snapshot action sets 'result', not 'snapshot-size'."""
ctx = testing.Context(KosmosCharm)
ctx.run(
ctx.on.action("snapshot", params={"filename": "db-snapshot.tar.gz"}),
testing.State(),
)
# The doc's handler sets results to {'result': msg}, so 'snapshot-size'
# is absent. The documented assertion ``'snapshot-size' in
# ctx.action_results`` would therefore fail.
assert "snapshot-size" not in ctx.action_results
assert ctx.action_results == {"result": "Stored snapshot in db-snapshot.tar.gz."}
Loading